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

Vọc Vạch Playwright

[Vọc Playwright] Global setup và Global teardown

https://playwright.dev/docs/test-global-setup-teardown

Giới thiệu

  • Global setup và teardown, hay hiểu đơn giản là chạy cài đặt một đoạn code trước tất cả các test chạy, và chạy dọn dẹp (teardown) sau khi tất cả các test chạy.
  • Có hai cách để thực hiện điều này:
    • Config global setup file ở trong file playwright.config.ts
    • Sử dụng project dependency.

Cách 1: Sử dụng project dependency

  • Project dependency là danh sách các project cần chạy trước khi các test trong một project chạy.
  • Sử dụng project dependency để cài đặt global setup và teardowwn khá tiện.

Setup

  • Xét ví dụ sau
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  // ...
  projects: [
    {
      name: 'setup db',
      testMatch: /global\.setup\.ts/,
    },
    // {
    //   other project
    // }
  ]
});
  • Ở đây chúng ta thêm project setup db, sẽ chạy file global.setup.ts. Bây giờ sẽ cho project Desktop Chrome depend vào project setup db này:
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  // ...
  projects: [
    {
      name: 'setup db',
      testMatch: /global\.setup\.ts/,
    },
    {
      name: 'chromium with db',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup db'],
    },
  ]
});
  • Trong file global.setup.ts chỉ cần định nghĩa đơn giản
import { test as setup } from '@playwright/test';

setup('create new database', async ({ }) => {
  console.log('creating new database...');
  // Initialize the database
});
  • Lúc này bạn chỉ cần viết test bình thường là được. Phần code trong setup đã được chạy trước
import { test, expect } from '@playwright/test';

test('menu', async ({ page }) => {
  // Your test that depends on the database
});

Teardown

  • Để setup teardown thì bạn cần:
    • Tạo một project mới.
    • Cho project setup teardown vào project này.
  • Xét ví dụ sau
// playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  // ...
  projects: [
    {
      name: 'setup db',
      testMatch: /global\.setup\.ts/,
      teardown: 'cleanup db',
    },
    {
      name: 'cleanup db',
      testMatch: /global\.teardown\.ts/,
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup db'],
    },
  ]
});
  • Như bạn thấy, project “setup db” sẽ teardown vào project “cleanup db”.
  • Và code của file teardown như sau:
import { test as teardown } from '@playwright/test';

teardown('delete database', async ({ }) => {
  console.log('deleting test database...');
  // Delete the database
});

Tham khảo thêm một số ví dụ

Cách 2: setup ở file playwright.config.ts

  • Viết code ra 2 file:
    • global-setup.ts: file chứa code setup
    • global-teardown.ts: file chứa code teardown
  • Config trong file playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});
  • File setup và teardown chỉ export ra “MỘT” function duy nhất thôi.
  • Ví dụ:
// global-setup.ts
import { chromium, type FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const { baseURL, storageState } = config.projects[0].use;
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto(baseURL!);
  await page.getByLabel('User Name').fill('user');
  await page.getByLabel('Password').fill('password');
  await page.getByText('Sign in').click();
  await page.context().storageState({ path: storageState as string });
  await browser.close();
}

export default globalSetup;
  • Có file này rồi thì định nghĩa vào trong playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  use: {
    baseURL: 'http://localhost:3000/',
    storageState: 'state.json',
  },
});
  • Nhìn vào đoạn code trên, ta thấy global setup chạy xong sẽ lưu lại authenticated state vào file state.json. Do vậy mà tất cả các test sau này chạy sẽ đều được login sẵn luôn.
import { test } from '@playwright/test';

test('test', async ({ page }) => {
  await page.goto('/');
  // You are signed in!
});
  • Ngoài ra thì bạn cũng có thể setup sẵn một số data thông qua process.env. Ví dụ
// global.setup.ts

import type { FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  process.env.FOO = 'some data';
  // Or a more complicated data structure as JSON:
  process.env.BAR = JSON.stringify({ some: 'data' });
}

export default globalSetup;
// test.ts

import { test } from '@playwright/test';

test('test', async ({ page }) => {
  // environment variables which are set in globalSetup are only available inside test().
  const { FOO, BAR } = process.env;

  // FOO and BAR properties are populated.
  expect(FOO).toEqual('some data');

  const complexData = JSON.parse(BAR);
  expect(BAR).toEqual({ some: 'data' });
});
  • Như bạn thấy, trong ví dụ trên, tại global setup sẽ set FOO và BAR vào trong biến môi trường. Và ta có thể đọc các biến này ở trong test.

Capture trace fail trong quá trình global setup

  • Cái này mình cũng chưa hiểu kĩ lắm, để mình đọc thêm rồi update bài viết mới được
// global-setup.ts
import { chromium, type FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const { baseURL, storageState } = config.projects[0].use;
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();
  try {
    await context.tracing.start({ screenshots: true, snapshots: true });
    await page.goto(baseURL!);
    await page.getByLabel('User Name').fill('user');
    await page.getByLabel('Password').fill('password');
    await page.getByText('Sign in').click();
    await context.storageState({ path: storageState as string });
    await context.tracing.stop({
      path: './test-results/setup-trace.zip',
    });
    await browser.close();
  } catch (error) {
    await context.tracing.stop({
      path: './test-results/failed-setup-trace.zip',
    });
    await browser.close();
    throw error;
  }
}

export default globalSetup;

Trả lời