并行化和分片
Playwright Test 并行运行测试。为了实现这一点,它运行多个同时运行的工作进程。
- 默认情况下,测试文件并行运行。单个文件中的测试按顺序在同一工作进程中运行。
- 使用
test.describe.configure配置测试,以并行运行单个文件中的测试。 - 您可以使用 testProject.fullyParallel 或 testConfig.fullyParallel 配置整个项目,使所有文件中的所有测试并行运行。
- 要禁用并行化,请将 workers 数量限制为一个。
您可以控制并行工作进程的数量并限制失败次数以提高整个测试套件的效率。
工作进程
所有测试都在工作进程中运行。这些进程是 OS 进程,独立运行,由测试运行器编排。所有工作进程都有相同的环境,每个都启动自己的浏览器。
您无法在工作进程之间通信。Playwright Test 尽可能重用单个工作进程以加快测试速度,因此多个测试文件通常在单个工作进程中一个接一个地运行。
工作进程总是在测试失败后关闭,以保证后续测试的原始环境。
限制工作进程
从命令行:
npx playwright test --workers 4
在配置文件中:
- TypeScript
- JavaScript
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
// 在 CI 上限制工作进程数量,本地使用默认值
workers: process.env.CI ? 2 : undefined,
};
export default config;
// playwright.config.js
// @ts-check
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
// 在 CI 上限制工作进程数量,本地使用默认值
workers: process.env.CI ? 2 : undefined,
};
module.exports = config;
禁用并行化
您可以通过只允许一个工作进程来禁用任何并行化。在配置文件中设置 workers: 1 选项或将 --workers=1 传递给命令行。
npx playwright test --workers=1
并行化单个文件中的测试
默认情况下,单个文件中的测试按顺序运行。如果您在单个文件中有许多独立的测试,您可能希望使用 test.describe.configure([options]) 并行运行它们。
请注意,并行测试在单独的工作进程中执行,不能共享任何状态或全局变量。每个测试仅为自己执行所有相关钩子,包括 beforeAll 和 afterAll。
- TypeScript
- JavaScript
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });
const { test } = require('@playwright/test');
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ });
或者,您可以在配置文件中选择所有测试(或仅几个项目)进入此完全并行模式:
- TypeScript
- JavaScript
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
fullyParallel: true,
};
export default config;
// playwright.config.js
// @ts-check
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
fullyParallel: true,
};
module.exports = config;
串行模式
您可以将相互依赖的测试标注为串行。如果其中一个串行测试失败,所有后续测试都将被跳过。组中的所有测试将一起重试。
不建议使用串行模式。通常最好使您的测试隔离,以便它们可以独立运行。
- TypeScript
- JavaScript
// example.spec.ts
import { test, Page } from '@playwright/test';
// 将整个文件标注为串行。
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.dev/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
});
// @ts-check
const { test } = require('@playwright/test');
test.describe.configure({ mode: 'serial' });
/** @type {import('@playwright/test').Page} */
let page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
});
test.afterAll(async () => {
await page.close();
});
test('runs first', async () => {
await page.goto('https://playwright.dev/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
});
在多台机器之间分片测试
Playwright Test 可以对测试套件进行分片,以便可以在多台机器上执行。为此,将 --shard=x/y 传递给命令行。例如,要将套件分成三个分片,每个运行三分之一的测试:
npx playwright test --shard=1/3
npx playwright test --shard=2/3
npx playwright test --shard=3/3
这样您的测试套件完成速度快 3 倍。
限制失败和快速失败
您可以通过设置 maxFailures 配置选项或传递 --max-failures 命令行标志来限制整个测试套件中失败测试的数量。
使用"最大失败次数"运行时,Playwright Test 将在达到此失败测试数量后停止,并跳过尚未执行的任何测试。这对于避免在损坏的测试套件上浪费资源很有用。
传递命令行选项:
npx playwright test --max-failures=10
在配置文件中设置:
- TypeScript
- JavaScript
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
// 在 CI 上限制失败次数以节省资源
maxFailures: process.env.CI ? 10 : undefined,
};
export default config;
// playwright.config.js
// @ts-check
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
// 在 CI 上限制失败次数以节省资源
maxFailures: process.env.CI ? 10 : undefined,
};
module.exports = config;
工作进程索引和并行索引
每个工作进程被分配两个 ID:从 1 开始的唯一工作进程索引,以及介于 0 和 workers - 1 之间的并行索引。当工作进程重新启动时,例如在失败后,新的工作进程具有相同的 parallelIndex 和新的 workerIndex。
您可以从环境变量 process.env.TEST_WORKER_INDEX 和 process.env.TEST_PARALLEL_INDEX 读取索引,或通过 testInfo.workerIndex 和 testInfo.parallelIndex 访问它们。
控制测试顺序
Playwright Test 按声明顺序运行单个文件中的测试,除非您并行化单个文件中的测试。
跨文件的测试执行顺序没有保证,因为 Playwright Test 默认并行运行测试文件。但是,如果您禁用并行化,您可以通过按字母顺序命名文件或使用"测试列表"文件来控制测试顺序。
按字母顺序排序测试文件
当您禁用并行测试执行时,Playwright Test 按字母顺序运行测试文件。您可以使用某种命名约定来控制测试顺序,例如 001-user-signin-flow.spec.ts、002-create-new-document.spec.ts 等。
使用"测试列表"文件
您可以将测试放在多个文件的辅助函数中。考虑以下示例,其中测试不是直接在文件中定义的,而是在包装函数中定义的。
- TypeScript
- JavaScript
// feature-a.spec.ts
import { test, expect } from '@playwright/test';
export default function createTests() {
test('feature-a example test', async ({ page }) => {
// ... 测试代码在这里
});
}
// 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 }) => {
// ... 测试代码在这里
});
}
// feature-a.spec.js
const { test, expect } = require('@playwright/test');
module.exports = function createTests() {
test('feature-a example test', async ({ page }) => {
// ... 测试代码在这里
});
};
// feature-b.spec.js
const { test, expect } = require('@playwright/test');
module.exports = function createTests() {
test.use({ viewport: { width: 500, height: 500 } });
test('feature-b example test', async ({ page }) => {
// ... 测试代码在这里
});
};
您可以创建一个测试列表文件来控制测试顺序 - 首先运行 feature-b 测试,然后运行 feature-a 测试。请注意,每个测试文件都包装在一个 test.describe() 块中,该块调用定义测试的函数。这样 test.use() 调用只影响单个文件中的测试。
- TypeScript
- JavaScript
// 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);
// test.list.js
const { test } = require('@playwright/test');
test.describe(require('./feature-b.spec.js'));
test.describe(require('./feature-a.spec.js'));
现在禁用并行执行,将 workers 设置为 1,并指定您的测试列表文件。
- TypeScript
- JavaScript
// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
workers: 1,
testMatch: 'test.list.ts',
};
export default config;
// playwright.config.js
// @ts-check
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
workers: 1,
testMatch: 'test.list.js',
};
module.exports = config;
不要直接在辅助文件中定义测试。这可能导致意外结果,因为您的测试现在依赖于 import/require 语句的顺序。相反,将测试包装在一个函数中,该函数将由测试列表文件显式调用,如上例所示。