并行
简介
Playwright Test 并行运行测试。为了实现这一点,它同时运行多个工作进程。默认情况下,**测试文件** 是并行运行的。单个文件中的测试按顺序在同一个工作进程中运行。
- 您可以使用 `test.describe.configure` 配置测试,以并行运行 **单个文件中的测试**。
- 您可以使用 `testProject.fullyParallel` 或 `testConfig.fullyParallel` 配置 **整个项目**,使所有文件中的所有测试并行运行。
- 要 **禁用** 并行,请将 `工作进程数限制为一个。
您可以控制 `并行工作进程` 的数量,并 `限制整个测试套件中的失败次数` 以提高效率。
工作进程
所有测试都在工作进程中运行。这些进程是操作系统进程,独立运行,由测试运行器协调。所有工作进程都具有相同的环境,并且每个进程都启动自己的浏览器。
工作进程之间无法通信。Playwright Test 会尽可能多地重用单个工作进程以加快测试速度,因此通常在单个工作进程中一个接一个地运行多个测试文件。
工作进程总是在 `测试失败` 后关闭,以保证后续测试的原始环境。
限制工作进程数
您可以通过 `命令行` 或在 `配置文件` 中控制并行工作进程的最大数量。
从命令行
npx playwright test --workers 4
在配置文件中
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of workers on CI, use default locally
workers: process.env.CI ? 2 : undefined,
});
禁用并行
您可以通过在任何时候只允许一个工作进程来禁用任何并行。可以在配置文件中设置 `workers: 1` 选项,或者将 `--workers=1` 传递给命令行。
npx playwright test --workers=1
并行化单个文件中的测试
默认情况下,单个文件中的测试按顺序运行。如果单个文件中有很多独立的测试,您可能希望使用 `test.describe.configure()` 并行运行它们。
请注意,并行测试在单独的工作进程中执行,并且无法共享任何状态或全局变量。每个测试都只为自己执行所有相关的钩子,包括 `beforeAll` 和 `afterAll`。
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });
或者,您可以在配置文件中选择将所有测试都加入到这种完全并行模式。
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
});
您也可以为少数几个项目选择完全并行模式
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
在配置文件中设置
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of failures on CI to save resources
maxFailures: process.env.CI ? 10 : undefined,
});
工作进程索引和并行索引
每个工作进程都分配了两个 ID:一个从 1 开始的唯一工作进程索引,以及一个介于 `0` 和 `workers - 1` 之间的并行索引。当工作进程重新启动时(例如在失败之后),新的工作进程具有相同的 `parallelIndex` 和一个新的 `workerIndex`。
您可以从环境变量 `process.env.TEST_WORKER_INDEX` 和 `process.env.TEST_PARALLEL_INDEX` 中读取索引,或者通过 `testInfo.workerIndex` 和 `testInfo.parallelIndex` 访问它们。
在并行工作进程之间隔离测试数据
您可以利用上面提到的 `process.env.TEST_WORKER_INDEX` 或 `testInfo.workerIndex` 在不同工作进程上运行的测试之间隔离数据库中的用户数据。工作进程运行的所有测试都重用同一个用户。
创建 `playwright/fixtures.ts` 文件,该文件将 `创建 `dbUserName` fixture` 并在测试数据库中初始化一个新用户。使用 `testInfo.workerIndex` 来区分不同的工作进程。
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 文件导入 `test`,而不是 `@playwright/test`。
// 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.ts`、`002-create-new-document.spec.ts` 等等。
使用 “测试列表” 文件
不鼓励使用测试列表,仅作为尽力而为的支持。某些功能(例如 VS Code 扩展和 tracing)可能无法与测试列表一起正常工作。
您可以将测试放在多个文件中的辅助函数中。考虑以下示例,其中测试不是直接在文件中定义的,而是在包装函数中定义的。
import { test, expect } from '@playwright/test';
export default function createTests() {
test('feature-a example test', async ({ page }) => {
// ... test goes here
});
}
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()` 调用仅影响单个文件中的测试。
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);
现在通过将工作进程数设置为 1 **禁用并行执行**,并指定您的测试列表文件。
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 1,
testMatch: 'test.list.ts',
});
不要直接在辅助文件中定义测试。这可能会导致意外的结果,因为您的测试现在依赖于 `import`/`require` 语句的顺序。相反,请将测试包装在一个函数中,该函数将由测试列表文件显式调用,如上面的示例所示。