重试
介绍
¥Introduction
测试重试是一种在测试失败时自动重新运行测试的方法。当测试不稳定并且间歇性失败时,这非常有用。测试重试在 配置文件 中配置。
¥Test retries are a way to automatically re-run a test when it fails. This is useful when a test is flaky and fails intermittently. Test retries are configured in the configuration file.
失败
¥Failures
Playwright Test 在工作进程中运行测试。这些进程是操作系统进程,独立运行,由测试运行器编排。所有工作线程都有相同的环境,并且每个工作线程都启动自己的浏览器。
¥Playwright Test runs tests in worker processes. These processes are OS processes, running independently, orchestrated by the test runner. All workers have identical environments and each starts its own browser.
考虑以下片段:
¥Consider the following snippet:
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 () => { /* ... */ });
});
当所有测试通过后,它们将在同一个工作进程中按顺序运行。
¥When all tests pass, they will run in order in the same worker process.
-
工作进程启动
¥Worker process starts
-
beforeAll
钩子运行¥
beforeAll
hook runs -
first good
次通过¥
first good
passes -
second flaky
次通过¥
second flaky
passes -
third good
次通过¥
third good
passes -
afterAll
钩子运行¥
afterAll
hook runs
-
如果任何测试失败,Playwright Test 将丢弃整个工作进程以及浏览器,并启动一个新的工作进程。从下一个测试开始,测试将在新的工作进程中继续进行。
¥Should any test fail, Playwright Test will discard the entire worker process along with the browser and will start a new one. Testing will continue in the new worker process starting with the next test.
-
工作进程 #1 启动
¥Worker process #1 starts
-
beforeAll
钩子运行¥
beforeAll
hook runs -
first good
次通过¥
first good
passes -
second flaky
失败¥
second flaky
fails -
afterAll
钩子运行¥
afterAll
hook runs
-
-
工作进程 #2 启动
¥Worker process #2 starts
-
beforeAll
钩子再次运行¥
beforeAll
hook runs again -
third good
次通过¥
third good
passes -
afterAll
钩子运行¥
afterAll
hook runs
-
如果启用 retries,第二个工作进程将通过重试失败的测试来启动,并从那里继续。
¥If you enable retries, second worker process will start by retrying the failed test and continue from there.
-
工作进程 #1 启动
¥Worker process #1 starts
-
beforeAll
钩子运行¥
beforeAll
hook runs -
first good
次通过¥
first good
passes -
second flaky
失败¥
second flaky
fails -
afterAll
钩子运行¥
afterAll
hook runs
-
-
工作进程 #2 启动
¥Worker process #2 starts
-
beforeAll
钩子再次运行¥
beforeAll
hook runs again -
重试
second flaky
并通过¥
second flaky
is retried and passes -
third good
次通过¥
third good
passes -
afterAll
钩子运行¥
afterAll
hook runs
-
该方案非常适合独立测试,并保证失败的测试不会影响健康的测试。
¥This scheme works perfectly for independent tests and guarantees that failing tests can't affect healthy ones.
重试
¥Retries
Playwright 支持测试重试。启用后,失败的测试将重试多次,直到通过,或达到最大重试次数。默认情况下,不会重试失败的测试。
¥Playwright supports test retries. When enabled, failing tests will be retried multiple times until they pass, or until the maximum number of retries is reached. By default failing tests are not retried.
# Give failing tests 3 retry attempts
npx playwright test --retries=3
你可以在配置文件中配置重试次数:
¥You can configure retries in the configuration file:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Give failing tests 3 retry attempts
retries: 3,
});
Playwright 测试将测试分类如下:
¥Playwright Test will categorize tests as follows:
-
"passed" - 第一次运行时通过的测试;
¥"passed" - tests that passed on the first run;
-
"flaky" - 第一次运行失败但重试时通过的测试;
¥"flaky" - tests that failed on the first run, but passed when retried;
-
"failed" - 第一次运行失败且所有重试均失败的测试。
¥"failed" - tests that failed on the first run and failed all retries.
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 在运行时检测重试,任何测试、钩子或夹具都可以访问该重试。下面是一个在重试之前清除某些服务器端状态的示例。
¥You can detect retries at runtime with testInfo.retry, which is accessible to any test, hook or fixture. Here is an example that clears some server-side state before a retry.
import { test, expect } from '@playwright/test';
test('my test', async ({ page }, testInfo) => {
if (testInfo.retry)
await cleanSomeCachesOnTheServer();
// ...
});
你可以使用 test.describe.configure() 指定对特定测试组或单个文件的重试。
¥You can specify retries for a specific group of tests or a single file with 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 }) => {
// ...
});
});
串行模式
¥Serial mode
使用 test.describe.serial() 对相关测试进行分组,以确保它们始终按顺序一起运行。如果其中一项测试失败,则跳过所有后续测试。组中的所有测试都会一起重试。
¥Use test.describe.serial() to group dependent tests to ensure they will always run together and in order. If one of the tests fails, all subsequent tests are skipped. All tests in the group are retried together.
考虑以下使用 test.describe.serial
的代码片段:
¥Consider the following snippet that uses 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 运行时,失败后的所有测试都会被跳过:
¥When running without retries, all tests after the failure are skipped:
-
工作进程#1:
¥Worker process #1:
-
beforeAll
钩子运行¥
beforeAll
hook runs -
first good
次通过¥
first good
passes -
second flaky
失败¥
second flaky
fails -
third good
被完全跳过¥
third good
is skipped entirely
-
当使用 retries 运行时,所有测试都会一起重试:
¥When running with retries, all tests are retried together:
-
工作进程#1:
¥Worker process #1:
-
beforeAll
钩子运行¥
beforeAll
hook runs -
first good
次通过¥
first good
passes -
second flaky
失败¥
second flaky
fails -
third good
被跳过¥
third good
is skipped
-
-
工作进程#2:
¥Worker process #2:
-
beforeAll
钩子再次运行¥
beforeAll
hook runs again -
first good
再次通过¥
first good
passes again -
second flaky
次通过¥
second flaky
passes -
third good
次通过¥
third good
passes
-
通常最好将测试隔离起来,这样它们就可以有效地运行并独立重试。
¥It is usually better to make your tests isolated, so they can be efficiently run and retried independently.
在测试之间重用单页
¥Reuse single page between tests
Playwright Test 为每个测试创建一个独立的 Page 对象。但是,如果你想在多个测试之间重用单个 Page 对象,你可以在 test.beforeAll() 中创建自己的对象并在 test.afterAll() 中关闭它。
¥Playwright Test creates an isolated Page object for each test. However, if you'd like to reuse a single Page object between multiple tests, you can create your own in test.beforeAll() and close it in 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.nodejs.cn/');
});
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.nodejs.cn/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
});