重试
简介
测试重试是一种在测试失败时自动重新运行测试的方法。当测试不稳定且间歇性失败时,这非常有用。测试重试可以在配置文件中进行配置。
失败
Playwright Test 在 worker 进程中运行测试。这些进程是操作系统进程,独立运行,由测试运行器协调。所有 worker 都具有相同的环境,每个 worker 都会启动自己的浏览器。
考虑以下代码片段:
import { test } from '@playwright/test';
test.describe('suite', () => {
test.beforeAll(async () => { /* ... */ });
test('first good', async ({ page }) => { /* ... */ });
test('second flaky', async ({ page }) => { /* ... */ });
test('third good', async ({ page }) => { /* ... */ });
test.afterAll(async () => { /* ... */ });
});
当所有测试通过时,它们将在同一个工作进程中按顺序运行。
- Worker process starts
beforeAll
钩子函数运行first good
通过second flaky
通过third good
通过afterAll
钩子函数运行
如果任何测试失败,Playwright Test将丢弃整个工作进程以及浏览器,并启动一个新的进程。测试将在新的工作进程中继续,从下一个测试开始。
- Worker process #1 starts
beforeAll
钩子函数运行first good
通过second flaky
失败afterAll
钩子函数运行
- Worker process #2 starts
beforeAll
钩子函数再次运行third good
通过afterAll
钩子函数运行
如果您启用重试,第二个工作进程将通过重试失败的测试来启动,并从那里继续。
- Worker process #1 starts
beforeAll
钩子函数运行first good
通过second flaky
失败afterAll
钩子函数运行
- Worker process #2 starts
beforeAll
钩子函数再次运行second flaky
重试后通过third good
通过afterAll
钩子函数运行
该方案非常适合独立测试,并确保失败的测试不会影响正常的测试。
重试机制
Playwright 支持测试重试功能。启用后,失败的测试将多次重试直到通过,或达到最大重试次数。默认情况下失败的测试不会重试。
# Give failing tests 3 retry attempts
npx playwright test --retries=3
您可以在配置文件中配置重试次数:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Give failing tests 3 retry attempts
retries: 3,
});
Playwright Test 将按以下方式对测试进行分类:
- "passed" - 首次运行通过的测试;
- "flaky" - 首次运行失败但重试后通过的测试;
- "failed" - 首次运行失败且所有重试均失败的测试。
Running 3 tests using 1 worker
✓ example.spec.ts:4:2 › first passes (438ms)
x example.spec.ts:5:2 › second flaky (691ms)
✓ example.spec.ts:5:2 › second flaky (522ms)
✓ example.spec.ts:6:2 › third passes (932ms)
1 flaky
example.spec.ts:5:2 › second flaky
2 passed (4s)
你可以在运行时通过testInfo.retry检测重试操作,该属性对任何测试、钩子或夹具都可用。下面是一个在重试前清除某些服务端状态的示例。
import { test, expect } from '@playwright/test';
test('my test', async ({ page }, testInfo) => {
if (testInfo.retry)
await cleanSomeCachesOnTheServer();
// ...
});
您可以使用test.describe.configure()为特定测试组或单个文件指定重试次数。
import { test, expect } from '@playwright/test';
test.describe(() => {
// All tests in this describe group will get 2 retry attempts.
test.describe.configure({ retries: 2 });
test('test 1', async ({ page }) => {
// ...
});
test('test 2', async ({ page }) => {
// ...
});
});
串行模式
使用 test.describe.serial() 将依赖测试分组,确保它们始终按顺序一起运行。如果其中一个测试失败,所有后续测试将被跳过。组内的所有测试会一起重试。
考虑以下使用test.describe.serial
的代码片段:
import { test } from '@playwright/test';
test.describe.configure({ mode: 'serial' });
test.beforeAll(async () => { /* ... */ });
test('first good', async ({ page }) => { /* ... */ });
test('second flaky', async ({ page }) => { /* ... */ });
test('third good', async ({ page }) => { /* ... */ });
在没有启用retries的情况下运行时,失败后的所有测试都会被跳过:
- Worker process #1:
beforeAll
钩子函数运行first good
通过second flaky
失败third good
被完全跳过
当使用retries运行时,所有测试会一起重试:
- Worker process #1:
beforeAll
钩子函数运行first good
通过second flaky
失败third good
被跳过
- Worker process #2:
beforeAll
钩子函数再次运行first good
再次通过second flaky
通过third good
通过
通常最好让你的测试保持隔离状态,这样它们可以高效地独立运行和重试。
在测试间复用单个页面
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('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();
});