Повторные попытки
Введение
Повторные попытки тестов – это способ автоматически перезапустить тест, если он не прошел. Это полезно, когда тест нестабилен и периодически не проходит. Повторные попытки тестов настраиваются в файле конфигурации.
Ошибки
Playwright Test запускает тесты в рабочих процессах. Эти процессы являются процессами ОС, работающими независимо, под управлением тестового раннера. Все рабочие процессы имеют идентичные окружения, и каждый из них запускает свой собственный браузер.
Рассмотрим следующий фрагмент:
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
- Выполняется хук
Если вы включите повторные попытки, второй рабочий процесс начнет с повторной попытки неудачного теста и продолжит с этого момента.
- Рабочий процесс #1 запускается
- Выполняется хук
beforeAll
first good
проходитsecond flaky
не проходит- Выполняется хук
afterAll
- Выполняется хук
- Рабочий процесс #2 запускается
- Выполняется хук
beforeAll
снова second flaky
повторяется и проходитthird good
проходит- Выполняется хук
afterAll
- Выполняется хук
Эта схема отлично работает для независимых тестов и гарантирует, что неудачные тесты не могут повлиять на успешные.
Повторные попытки
Playwright поддерживает повторные попытки тестов. Когда они включены, неудачные тесты будут повторяться несколько раз, пока не пройдут, или пока не будет достигнуто максимальное количество попыток. По умолчанию неудачные тесты не повторяются.
# Дать неудачным тестам 3 попытки
npx playwright test --retries=3
Вы можете настроить повторные попытки в файле конфигурации:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Дать неудачным тестам 3 попытки
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(() => {
// Все тесты в этой группе описания получат 2 попытки.
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 }) => { /* ... */ });
При запуске без повторных попыток, все тесты после неудачи пропускаются:
- Рабочий процесс #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 создает изолированный объект 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();
});