跳到主要内容

并行

简介

Playwright Test 并行运行测试。为了实现这一点,它同时运行多个 worker 进程。默认情况下,测试文件 是并行运行的。单个文件中的测试按顺序在同一个 worker 进程中运行。

您可以控制 并行 worker 进程的数量,并 限制整个测试套件中的失败数量以提高效率。

Worker 进程

所有测试都在 worker 进程中运行。这些进程是操作系统进程,独立运行,由测试运行器协调。所有 worker 都具有相同的环境,并且每个 worker 启动自己的浏览器。

worker 之间无法通信。Playwright Test 会尽可能重用单个 worker 以加快测试速度,因此通常多个测试文件会在单个 worker 中依次运行。

worker 总是在 测试失败后关闭,以保证后续测试的纯净环境。

限制 worker

您可以通过 命令行 或在 配置文件 中控制并行 worker 进程的最大数量。

从命令行

npx playwright test --workers 4

在配置文件中

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

禁用并行

您可以通过在任何时候只允许一个 worker 来禁用任何并行。可以在配置文件中设置 workers: 1 选项,或者将 --workers=1 传递给命令行。

npx playwright test --workers=1

并行化单个文件中的测试

默认情况下,单个文件中的测试按顺序运行。如果单个文件中有很多独立的测试,您可能希望使用 test.describe.configure() 并行运行它们。

请注意,并行测试在单独的 worker 进程中执行,并且无法共享任何状态或全局变量。每个测试都只为自己执行所有相关的 hooks,包括 beforeAllafterAll

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

test.describe.configure({ mode: 'parallel' });

test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });

或者,您可以在配置文件中选择将所有测试都加入到这种完全并行模式中

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

export default defineConfig({
fullyParallel: true,
});

您还可以选择仅为几个项目启用完全并行模式

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

串行模式

您可以将相互依赖的测试标记为串行。如果其中一个串行测试失败,则所有后续测试都将被跳过。组中的所有测试将一起重试。

注意

不建议使用串行。通常最好使您的测试隔离,以便它们可以独立运行。

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.net.cn/');
});

test('runs second', async () => {
await page.getByText('Get Started').click();
});

在多台机器之间分片测试

Playwright Test 可以分片测试套件,以便可以在多台机器上执行。有关更多详细信息,请参阅 分片指南

npx playwright test --shard=2/3

限制失败次数和快速失败

您可以通过设置 maxFailures 配置选项或传递 --max-failures 命令行标志来限制整个测试套件中失败测试的数量。

当设置了 “最大失败次数” 运行时,Playwright Test 将在达到此失败测试数量后停止,并跳过任何尚未执行的测试。这对于避免在损坏的测试套件上浪费资源很有用。

传递命令行选项

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 索引和并行索引

每个 worker 进程都被分配了两个 ID:一个从 1 开始的唯一 worker 索引,以及一个介于 0 和 workers - 1 之间的并行索引。当 worker 重新启动时,例如在失败后,新的 worker 进程具有相同的 parallelIndex 和一个新的 workerIndex

您可以从环境变量 process.env.TEST_WORKER_INDEXprocess.env.TEST_PARALLEL_INDEX 中读取索引,或者通过 testInfo.workerIndextestInfo.parallelIndex 访问它们。

在并行 worker 之间隔离测试数据

您可以利用上面提到的 process.env.TEST_WORKER_INDEXtestInfo.workerIndex 在不同 worker 上运行的测试之间隔离数据库中的用户数据。由 worker 运行的所有测试都重用同一个用户。

创建 playwright/fixtures.ts 文件,该文件将 创建 dbUserName fixture 并在测试数据库中初始化一个新用户。使用 testInfo.workerIndex 来区分不同的 worker。

playwright/fixtures.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}`;
// Initialize user in the database.
await createUserInTestDatabase(userName);
await use(userName);
// Clean up after the tests are done.
await deleteUserFromTestDatabase(userName);
}, { scope: 'worker' }],
});

现在,每个测试文件都应该从我们的 fixtures 文件而不是 @playwright/test 导入 test

tests/example.spec.ts
// Important: import our fixtures.
import { test, expect } from '../playwright/fixtures';

test('test', async ({ dbUserName }) => {
// Use the user name in the test.
});

控制测试顺序

Playwright Test 按照声明顺序运行单个文件中的测试,除非您 并行化单个文件中的测试

无法保证跨文件的测试执行顺序,因为默认情况下 Playwright Test 并行运行测试文件。但是,如果您 禁用并行,您可以通过按字母顺序命名文件或使用 “测试列表” 文件来控制测试顺序。

按字母顺序排序测试文件

当您**禁用并行测试执行**时,Playwright Test 会按字母顺序运行测试文件。您可以使用一些命名约定来控制测试顺序,例如 001-user-signin-flow.spec.ts002-create-new-document.spec.ts 等等。

使用 “测试列表” 文件

警告

不鼓励使用测试列表,并且仅作为尽力而为的支持。某些功能(例如 VS Code 扩展和跟踪)可能无法与测试列表正常工作。

您可以将测试放在多个文件中的辅助函数中。考虑以下示例,其中测试不是直接在文件中定义的,而是在包装函数中定义的。

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

您可以创建一个测试列表文件来控制测试顺序 - 首先运行 feature-b 测试,然后运行 feature-a 测试。请注意,每个测试文件是如何包装在调用定义测试的函数的 test.describe() 块中的。这样,test.use() 调用只会影响单个文件中的测试。

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

现在通过将 worker 设置为一个来**禁用并行执行**,并指定您的测试列表文件。

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

export default defineConfig({
workers: 1,
testMatch: 'test.list.ts',
});
注意

不要直接在辅助文件中定义测试。这可能会导致意外的结果,因为您的测试现在依赖于 import/require 语句的顺序。相反,请将测试包装在一个函数中,该函数将由测试列表文件显式调用,如上面的示例所示。