リトライ
概要
テストのリトライは、テストが失敗した場合に自動的に再実行する方法です。これは、テストが不安定で断続的に失敗する場合に役立ちます。テストのリトライは、設定ファイルで設定します。
失敗
Playwright Test は、ワーカプロセスでテストを実行します。これらのプロセスは OS プロセスであり、独立して実行され、テストランナーによって調整されます。すべてのワーカは同一の環境を持ち、それぞれが独自のブラウザを起動します。
次のスニペットを検討してください。
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 () => { /* ... */ });
});
すべてのテストが合格する場合、それらは同じワーカプロセス内で順番に実行されます。
- ワーカプロセスが開始
beforeAll
フックが実行first good
が合格second flaky
が合格third good
が合格afterAll
フックが実行
いずれかのテストが失敗した場合、Playwright Test はブラウザとともにワーカプロセス全体を破棄し、新しいワーカプロセスを開始します。テストは、次のテストから始まる新しいワーカプロセスで続行されます。
- ワーカプロセス #1 が開始
beforeAll
フックが実行first good
が合格second flaky
が失敗afterAll
フックが実行
- ワーカプロセス #2 が開始
beforeAll
フックが再度実行third good
が合格afterAll
フックが実行
リトライを有効にすると、2 番目のワーカプロセスは、失敗したテストをリトライすることから開始し、そこから続行します。
- ワーカプロセス #1 が開始
beforeAll
フックが実行first good
が合格second flaky
が失敗afterAll
フックが実行
- ワーカプロセス #2 が開始
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()を使用して、依存関係のあるテストをグループ化し、常に一緒に順番に実行されるようにします。テストの 1 つが失敗した場合、後続のすべてのテストはスキップされます。グループ内のすべてのテストはまとめてリトライされます。
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 }) => { /* ... */ });
リトライなしで実行すると、失敗後のすべてのテストがスキップされます。
- ワーカプロセス #1
beforeAll
フックが実行first good
が合格second flaky
が失敗third good
が完全にスキップされます。
リトライありで実行すると、すべてのテストがまとめてリトライされます。
- ワーカプロセス #1
beforeAll
フックが実行first good
が合格second flaky
が失敗third good
がスキップされます。
- ワーカプロセス #2
beforeAll
フックが再度実行first good
が再度合格second flaky
が合格third good
が合格
通常、テストを独立させる方が、効率的に実行およびリトライできるため、より良い方法です。
テスト間で単一ページを再利用する
Playwright Test は、各テストに対して独立したページオブジェクトを作成します。ただし、複数のテスト間で単一のページオブジェクトを再利用したい場合は、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.dokyumento.jp/');
});
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.dokyumento.jp/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
});