https://playwright.dev/docs/test-parallel
Giới thiệu
- Playwright Test chạy các test song song. Để đạt được điều này, nó chạy một vài worker cùng một lúc.
- Mặc định thì các test nằm ở các file khác nhau sẽ chạy song song ở các worker khác nhau (nghĩa là cùng 1 file thì chạy cùng worker).
- Bạn có thể config
test.describe.configure
để chạy parallel trên một file. - Bạn có thể cấu hình cho toàn bộ project chạy parallel bằng cách dùng
testProject.fullyParallel
haytestConfig.fullyParallel
. - Để disable parallelism, giới hạn số lượng worker về 1
- Bạn có thể config
- Bạn có thể kiểm soát số lượng worker song song, hay cả số lượng fail nữa nha.
Worker process
- Các test được chạy trong các worker process, còn các worker process được chạy trong các process của hệ điều hành; chạy một cách độc lập, được điều khiển bởi test runner.
- Tất cả các worker đều có môi trường độc lập với nhau, có browser riêng.
- Bạn không thể giao tiếp với các worker được. Playwright Test sẽ cố gắng tái sử dụng worker nhiều nhất có thể.
- Worker sẽ tắt ngay sau khi test fail để đảm bảo tính nguyên sơ (đại khái giống kiểu sạch sẽ) của môi trường.
Giới hạn worker
- Có thể giới hạn bằng cách thêm vào configuration file hoặc command line.
- Ví dụ với command line
npx playwright test --workers 4
- Ví dụ với configuration file:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of workers on CI, use default locally
workers: process.env.CI ? 2 : undefined,
});
Tắt parallelism
- Thì set số lượng worker về 1 là xong.
Parallelize test trong một file.
- Mặc định thì trong 1 file, test sẽ chạy tuần tự.
- Nếu vẫn muốn chạy song song thì dùng
test.describe.configure()
. - Lưu ý rằng các test chạy ở worker khác nhau thì không thể share state hay global variable đâu nhé. Các hooks cũng chạy riêng luôn.
- Ví dụ beforeAll sẽ chạy mỗi worker một lần.
- Vì vậy phải hiểu bản chất của config parallelize này, không là gặp lỗi mà không hiểu tại sao.
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });
- Ngoài ra thì bạn config ở file config cũng được
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
});
- Thậm chí có thẻ chỉ config cho một project cụ thể nào đó
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// runs all tests in all files of a specific project in parallel
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
fullyParallel: true,
},
]
});
Serial mode
- Khi bạn dùng mode này, bạn sẽ quy ước rằng: các test này liên quan đến nhau.
- Nếu fail 1 test thì sẽ skip toàn bộ các test còn lại.
- Nếu retry thì sẽ retry tòan bộ cùng nhau luôn
- Lưu ý rằng không nên lạm dụng config này. Về cơ bản thì các test nên cố gắng độc lập với nhau, để có thể chạy độc lập được.
import { test, type Page } from '@playwright/test';
// Annotate entire file as serial.
test.describe.configure({ mode: 'serial' });
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
});
test.afterAll(async () => {
await page.close();
});
test('runs first', async () => {
await page.goto('https://playwright.dev/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
});
Chia test cho nhiều máy khác nhau
- Anh em cứ hiểu worker là cùng một máy, chạy đa luồng; còn sharding là chạy nhiều máy khác nhau.
- Phần này thì đến bài sharding tôi sẽ nói kĩ hơn nha.
Giới hạn số test fail và fail nhanh hơn
- Bạn có thể giới hạn số test fail bằng cách sử dụng
maxFailures
trong file config, hoặc thêm--max-failures
ở command line
npx playwright test --max-failures=10
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of failures on CI to save resources
maxFailures: process.env.CI ? 10 : undefined,
});
Worker index và parallel index
- Mỗi worker process được gắn với 2 loại id khác nhau:
- unique worker index: bắt đầu từ 1
- parallel index: bắt đầu từ 0 đến workers – 1
- Khi một worker restart (ví dụ trong case fail), một worker process mới được tạo ra, sẽ có một worker index mới và chung parallel index với process trước đó.
- Hai thông tin này có sẵn trong testInfo object để dùng.
Isolate test data giữa các parallel worker.
- Ví dụ bạn có fixture tạo ra user chẳng hạn.
- Bây giờ nếu bạn để default thì fixture chạy riêng biệt trên mỗi worker ~> có thể tạo trùng user.
- Cách xử lý là thêm random name vào tên user, hoặc sử dụng workerIndex, sẽ cho ra được unique username.
// playwright.config.ts
import { test as baseTest, expect } from '@playwright/test';
// Import project utils for managing users in the test database.
import { createUserInTestDatabase, deleteUserFromTestDatabase } from './my-db-utils';
export * from '@playwright/test';
export const test = baseTest.extend<{}, { dbUserName: string }>({
// Returns db user name unique for the worker.
dbUserName: [async ({ }, use) => {
// Use workerIndex as a unique identifier for each worker.
const userName = `user-${test.info().workerIndex}`;
// Inialize user in the database.
await createUserInTestDatabase(userName);
await use(userName);
// Clean up after the tests are done.
await deleteUserFromTestDatabase(userName);
}, { scope: 'worker' }],
});
Kiểm soát thứ tự chạy test
- Mặc định thì các test trong một file sẽ chạy theo thứ tự được khai báo.
- Khi disable parallel đi thì Playwright sẽ chạy test theo thứ tự bảng chữ cái.
- Bạn có thể sử dụng naming convention cho file để kiểm soát thứ tự chạy. Ví dụ:
001-user-signin.spec.ts
002-create-new-docuemnt.spec.ts
Sử dụng “test list” file
- Lưu ý: test list không nên dùng lắm, vì một số tính năng như VSCode Extension và tracing sẽ không hoạt động đúng với test lists.
- Nhìn ví dụ dưới đây
// feature-a.spec.ts
import { test, expect } from '@playwright/test';
export default function createTests() {
test('feature-a example test', async ({ page }) => {
// ... test goes here
});
}
// feature-b.spec.ts
import { test, expect } from '@playwright/test';
export default function createTests() {
test.use({ viewport: { width: 500, height: 500 } });
test('feature-b example test', async ({ page }) => {
// ... test goes here
});
}
// test.list.ts
import { test } from '@playwright/test';
import featureBTests from './feature-b.spec.ts';
import featureATests from './feature-a.spec.ts';
test.describe(featureBTests);
test.describe(featureATests);
- Thêm một lưu ý nữa: không nên định nghĩa test ngay trong helper file (ví dụ file
playwright.config.ts
) mà nên tách riêng ra một file.- Trong ví dụ trên là file
test.list.ts
- Lí do: có thể sẽ gây ra kết quả không mong muốn, vì nó phụ thuộc vào thứ tự của mệnh đề
import/require
- Trong ví dụ trên là file
Trả lời