重试机制
简介
测试重试是一种在测试失败时自动重新运行测试的方法。当测试不稳定并且偶尔失败时,这非常有用。测试重试可以在配置文件中进行配置。
失败处理
Playwright Test 在 worker 进程中运行测试。这些进程是操作系统进程,独立运行,由测试运行器协调。所有 worker 拥有相同的环境,并且每个 worker 都会启动自己的浏览器。
请参考以下代码片段:
import { test } from '@playwright/test';
test.describe('套件', () => {
test.beforeAll(async () => { /* ... */ });
test('第一个正常', async ({ page }) => { /* ... */ });
test('第二个不稳定', async ({ page }) => { /* ... */ });
test('第三个正常', async ({ page }) => { /* ... */ });
test.afterAll(async () => { /* ... */ });
});
当所有测试通过时,它们会按顺序在同一个 worker 进程中运行。
- Worker 进程启动
- 执行
beforeAll
钩子 第一个正常
通过第二个不稳定
通过第三个正常
通过- 执行
afterAll
钩子
- 执行
如果有任何测试失败,Playwright Test 会丢弃整个 worker 进程及其浏览器,并启动一个新的 worker 进程。测试将在新的 worker 进程中从下一个测试继续执行。
- Worker 进程 #1 启动
- 执行
beforeAll
钩子 第一个正常
通过第二个不稳定
失败- 执行
afterAll
钩子
- 执行
- Worker 进程 #2 启动
- 再次执行
beforeAll
钩子 第三个正常
通过- 执行
afterAll
钩子
- 再次执行
如果你启用了重试机制,第二个 worker 进程会从重试失败的测试开始,并从那里继续执行。
- Worker 进程 #1 启动
- 执行
beforeAll
钩子 第一个正常
通过第二个不稳定
失败- 执行
afterAll
钩子
- 执行
- Worker 进程 #2 启动
- 再次执行
beforeAll
钩子 第二个不稳定
被重试并通过第三个正常
通过- 执行
afterAll
钩子
- 再次执行
这种方案非常适用于相互独立的测试,并保证失败的测试不会影响健康的测试。
重试机制
Playwright 支持测试重试。启用后,失败的测试会被多次重试直到通过,或者达到最大重试次数为止。默认情况下,失败的测试不会被重试。
# 让失败的测试最多重试 3 次
npx playwright test --retries=3
你可以在配置文件中配置重试次数:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// 让失败的测试最多重试 3 次
retries: 3,
});
Playwright Test 会将测试分为以下几类:
- "通过" - 首次运行就通过的测试;
- "不稳定" - 首次运行失败,但重试后通过的测试;
- "失败" - 首次运行失败且所有重试都失败的测试。
使用 1 个 worker 运行 3 个测试
✓ example.spec.ts:4:2 › 第一个通过 (438ms)
x example.spec.ts:5:2 › 第二个不稳定 (691ms)
✓ example.spec.ts:5:2 › 第二个不稳定 (522ms)
✓ example.spec.ts:6:2 › 第三个通过 (932ms)
1 个不稳定
example.spec.ts:5:2 › 第二个不稳定
2 个通过 (4s)
你可以通过 testInfo.retry 在运行时检测是否为重试,在任何测试、钩子或 fixture 中都可以访问。下面是一个在重试前清理服务器端状态的示例。
import { test, expect } from '@playwright/test';
test('我的测试', async ({ page }, testInfo) => {
if (testInfo.retry)
await cleanSomeCachesOnTheServer();
// ...
});
你可以通过 test.describe.configure() 为特定的测试组或单个文件指定重试次数。
import { test, expect } from '@playwright/test';
test.describe(() => {
// 此 describe 分组下的所有测试都会有 2 次重试机会。
test.describe.configure({ retries: 2 });
test('测试 1', async ({ page }) => {
// ...
});
test('测试 2', async ({ page }) => {
// ...
});
});
串行模式
使用 test.describe.serial() 可以将依赖的测试分组,确保它们总是一起且按顺序运行。如果其中一个测试失败,后续的所有测试都会被跳过。分组内的所有测试会一起被重试。
请参考以下使用 test.describe.serial
的代码片段:
import { test } from '@playwright/test';
test.describe.configure({ mode: 'serial' });
test.beforeAll(async () => { /* ... */ });
test('第一个正常', async ({ page }) => { /* ... */ });
test('第二个不稳定', async ({ page }) => { /* ... */ });
test('第三个正常', async ({ page }) => { /* ... */ });
当不启用重试机制时,失败后的所有测试都会被跳过:
- Worker 进程 #1:
- 执行
beforeAll
钩子 第一个正常
通过第二个不稳定
失败第三个正常
被完全跳过
- 执行
当启用重试机制时,所有测试会一起被重试:
- Worker 进程 #1:
- 执行
beforeAll
钩子 第一个正常
通过第二个不稳定
失败第三个正常
被跳过
- 执行
- Worker 进程 #2:
- 再次执行
beforeAll
钩子 第一个正常
再次通过第二个不稳定
通过第三个正常
通过
- 再次执行
通常建议让你的测试相互隔离,这样可以高效地独立运行和重试。
在多个测试间复用单个页面
Playwright Test 会为每个测试创建一个独立的 Page 对象。不过,如果你希望在多个测试之间复用同一个 Page 对象,可以在 test.beforeAll() 中自行创建,并在 test.afterAll() 中关闭它。
- TypeScript
- JavaScript
import { test, type 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('第一个运行', async () => {
await page.goto('https://playwright.dev/');
});
test('第二个运行', 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('第一个运行', async () => {
await page.goto('https://playwright.dev/');
});
test('第二个运行', async () => {
await page.getByText('Get Started').click();
});