Học Playwright tiếng Việt, Cộng đồng Playwright cho người Việt

Vọc Vạch Playwright

[Vọc Playwright] Parallelism

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 hay testConfig.fullyParallel.
    • Để disable parallelism, giới hạn số lượng worker về 1
  • 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

Trả lời