全局设置和拆卸

简介

配置全局设置和拆卸有两种方式:使用全局设置文件并在配置中通过 globalSetup 设置,或使用【项目依赖】。通过项目依赖,你可以定义一个在所有其他项目之前运行的项目。这是配置全局设置的推荐方式,因为通过项目依赖,你的 HTML 报告会显示全局设置,追踪查看器会记录设置的追踪,且可以使用 fixtures。

选项 1:项目依赖

【项目依赖】是一个项目列表,这些项目需要在另一个项目的测试运行之前先执行。它们对于配置全局设置操作非常有用,因为一个项目可以依赖于先运行另一个项目。使用依赖关系可以使全局设置生成追踪和其他工件。

设置

首先,我们添加一个名为 "setup db" 的新项目。然后,我们给它设置 testProject.testMatch 属性,以匹配名为 global.setup.ts 的文件:

playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  // ...
  projects: [
    {
      name: 'setup db',
      testMatch: /global\.setup\.ts/,
    },
    // {
    //   other project
    // }
  ]
});

然后,我们在依赖于设置项目的项目中添加 testProject.dependencies 属性,并将我们在前一步定义的依赖项目的名称传递到数组中:

playwright.config.ts
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'],
    },
  ]
});

在这个例子中,'chromium with db' 项目依赖于 'setup db' 项目。然后我们创建一个设置测试,存储在项目的根目录下(请注意,设置和拆卸代码必须作为常规测试定义,使用 test() 函数调用):

tests/global.setup.ts
import { test as setup } from '@playwright/test';

setup('create new database', async ({ }) => {
  console.log('creating new database...');
  // Initialize the database
});
tests/menu.spec.ts
import { test, expect } from '@playwright/test';

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

拆卸

你可以通过在设置项目中添加 testProject.teardown 属性来拆卸你的设置。这样会在所有依赖项目运行之后执行。

首先,我们在设置项目中添加 testProject.teardown 属性,并为其命名为 'cleanup db',这是我们在上一步中为拆卸项目指定的名称:

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'],
    },
  ]
});

然后,我们在项目的 tests 目录中创建一个 global.teardown.ts 文件。这个文件将在所有测试运行完毕后,用于从数据库中删除数据。

tests/global.teardown.ts
import { test as teardown } from '@playwright/test';

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

更多例子

有关更详细的示例,请查看:

选项 2:配置 globalSetupglobalTeardown

你可以在【配置文件】中使用 globalSetup 选项,在运行所有测试之前进行一次性设置。全局设置文件必须导出一个接受配置对象的单一函数。该函数将在所有测试之前运行一次。

类似地,使用 globalTeardown 在所有测试之后运行一次。或者,让 globalSetup 返回一个将作为全局清理函数的函数。你可以通过环境变量将数据(如端口号、认证令牌等)从全局设置传递给测试。

使用 globalSetupglobalTeardown 不会生成跟踪或其他输出文件,且配置文件中指定的选项(如 headlesstestIdAttribute)不会被应用。如果你想生成跟踪和输出文件,并应用配置选项,请使用项目依赖。

playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});

示例

这是一个全局设置示例,展示了如何一次性进行身份验证并在测试中重用认证状态。它使用了配置文件中的 baseURLstorageState 选项。

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;

在配置文件中指定 globalSetupbaseURLstorageState

playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  use: {
    baseURL: 'http://localhost:3000/',
    storageState: 'state.json',
  },
});

测试已经在认证状态下开始,因为我们指定了由全局设置填充的 storageState

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

test('test', async ({ page }) => {
  await page.goto('/');
  // You are signed in!
});

你可以通过 process.env 将任意数据从全局设置文件传递给测试,使其可用。

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;

测试可以访问在全局设置中设置的 process.env 属性。

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' });
});

在全局设置期间捕获故障跟踪

在某些情况下,捕获全局设置过程中遇到的失败的跟踪信息可能会很有用。为了做到这一点,您必须在设置中【开始跟踪】,并确保在错误发生之前【停止跟踪】。可以通过将设置代码包装在 try…​catch 块中来实现这一点。以下是一个扩展全局设置示例以捕获跟踪的示例。

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;