全局 setup 和 teardown
引言
配置全局 setup 和 teardown 有两种方法:使用全局 setup 文件并在配置中设置 globalSetup
选项,或者使用 项目依赖。使用项目依赖时,您可以定义一个在所有其他项目之前运行的项目。这是推荐的方法,因为它与 Playwright 测试运行器集成得更好:您的 HTML 报告将包含全局 setup,会记录跟踪,并且可以使用 fixtures。这两种方法的详细比较见下表。
特性 | 项目依赖 (推荐) | globalSetup (配置选项) |
---|---|---|
在所有测试之前运行 | ✅ 是 | ✅ 是 |
HTML 报告可见性 | ✅ 显示为一个独立项目 | ❌ 不显示 |
跟踪记录 | ✅ 提供完整跟踪 | ❌ 不支持 |
Playwright fixtures | ✅ 完全支持 | ❌ 不支持 |
浏览器管理 | ✅ 通过 browser fixture | ❌ 完全手动通过 browserType.launch() |
并行和重试 | ✅ 通过标准配置支持 | ❌ 不适用 |
配置选项,如 headless 或 testIdAttribute | ✅ 自动应用 | ❌ 忽略 |
选项 1:项目依赖
项目依赖 是一系列需要在另一个项目的测试运行之前运行的项目。它们对于配置全局 setup 操作非常有用,以便一个项目依赖于此先运行。使用依赖项可以使全局 setup 生成跟踪和其他 artifacts。
Setup
首先,我们添加一个名为 'setup db' 的新项目。然后,我们为其设置 testProject.testMatch 属性,以匹配名为 global.setup.ts
的文件。
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
// ...
projects: [
{
name: 'setup db',
testMatch: /global\.setup\.ts/,
},
// {
// other project
// }
]
});
然后,我们在依赖于 setup 项目的项目中添加 testProject.dependencies 属性,并将我们在上一步中定义的依赖项目的名称添加到数组中。
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' 项目。然后,我们在项目的根目录创建一个 setup 测试(注意 setup 和 teardown 代码必须通过调用 test() 函数定义为常规测试)。
import { test as setup } from '@playwright/test';
setup('create new database', async ({ }) => {
console.log('creating new database...');
// Initialize the database
});
import { test, expect } from '@playwright/test';
test('menu', async ({ page }) => {
// Your test that depends on the database
});
Teardown
您可以通过向 setup 项目添加 testProject.teardown 属性来执行 setup 的 teardown 操作。这将在所有依赖项目运行后运行。
首先,我们向 setup 项目添加 testProject.teardown 属性,其名称为 'cleanup db',这是我们在上一步中为 teardown 项目指定的名称。
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
文件。此文件将在所有测试运行后用于从数据库中删除数据。
import { test as teardown } from '@playwright/test';
teardown('delete database', async ({ }) => {
console.log('deleting test database...');
// Delete the database
});
测试过滤
所有测试过滤选项,例如 --grep
/--grep-invert
、--shard
、直接在命令行中按位置过滤或使用 test.only()
,都直接选择要运行的主要测试。如果这些测试属于具有依赖项的项目,则这些依赖项中的所有测试也将运行。
您可以传递 --no-deps
命令行选项来忽略所有依赖项和 teardowns。将只运行您直接选择的项目。
更多示例
有关更多详细示例,请查看
- 我们的身份验证指南
- 我们的博客文章 如何在 Playwright 中使用项目依赖更好地进行全局 setup,并重用登录状态
- v1.31 发布视频 以观看演示
选项 2:配置 globalSetup 和 globalTeardown
您可以使用配置文件中的 globalSetup
选项,在运行所有测试之前一次性进行 setup。全局 setup 文件必须导出一个接受 config 对象的单个函数。此函数将在所有测试之前运行一次。
类似地,使用 globalTeardown
在所有测试之后运行一次。或者,让 globalSetup
返回一个函数,该函数将用作全局 teardown。您可以使用环境变量将端口号、身份验证令牌等数据从全局 setup 传递给测试。
import { defineConfig } from '@playwright/test';
export default defineConfig({
globalSetup: require.resolve('./global-setup'),
globalTeardown: require.resolve('./global-teardown'),
});
示例
这是一个全局 setup 示例,它一次性进行身份验证,并在测试中重用身份验证状态。它使用了配置文件中的 baseURL
和 storageState
选项。
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;
在配置文件中指定 globalSetup
、baseURL
和 storageState
。
import { defineConfig } from '@playwright/test';
export default defineConfig({
globalSetup: require.resolve('./global-setup'),
use: {
baseURL: 'http://localhost:3000/',
storageState: 'state.json',
},
});
测试开始时已通过身份验证,因为我们指定了由全局 setup 填充的 storageState
。
import { test } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('/');
// You are signed in!
});
您可以通过 process.env
将全局 setup 文件中的任意数据设置为环境变量,以便在测试中使用。
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;
测试可以访问全局 setup 中设置的 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' });
});
捕获全局 setup 期间的失败跟踪
在某些情况下,捕获全局 setup 期间遇到的失败跟踪可能会很有用。为此,您必须在 setup 中开始跟踪,并且必须确保如果在错误被抛出之前发生错误,您能够停止跟踪。这可以通过将 setup 包装在 try...catch
块中来实现。以下是一个扩展全局 setup 示例以捕获跟踪的示例。
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;