Test retries automatically re-run failed tests to handle flaky tests and improve test reliability. Playwright provides built-in retry mechanisms with detailed tracking.
test.describe('Flaky suite', () => { test.describe.configure({ retries: 3 }); test('flaky test', async ({ page }) => { // This test will be retried up to 3 times });});
test('example', async ({ page }, testInfo) => { console.log(`Attempt ${testInfo.retry + 1}`);});// With retries: 2// First run (retry: 0) - FAIL// Second run (retry: 1) - FAIL // Third run (retry: 2) - PASS -> Test passes
function outcome(): 'skipped' | 'expected' | 'unexpected' | 'flaky' { // 'expected': All attempts passed or test marked as .skip/.fixme // 'unexpected': All attempts failed // 'flaky': Failed initially, then passed // 'skipped': Test was skipped}
test('check retry', async ({ page }, testInfo) => { console.log(testInfo.retry); // Current retry number (0-based) console.log(testInfo.project.retries); // Max retries configured if (testInfo.retry > 0) { // This is a retry attempt console.log('Test is being retried'); }});
export default defineConfig({ use: { video: 'retain-on-failure', // Keep video if any attempt fails video: 'on-first-retry', // Record only on first retry },});
# Override config retriesnpx playwright test --retries=3# No retries (for debugging)npx playwright test --retries=0# Run only failed tests from last runnpx playwright test --last-failed
export default defineConfig({ failOnFlakyTests: true, retries: 2,});// Test that passes on retry will be marked as failure// Useful for ensuring tests are not flaky before merging
# CLI optionnpx playwright test --fail-on-flaky-tests
From src/common/config.ts:191:Different from retries - runs tests multiple times regardless of outcome:
export default defineConfig({ repeatEach: 3, // Run each test 3 times});
test('stability test', async ({ page }, testInfo) => { console.log(`Run ${testInfo.repeatEachIndex + 1}/3`); // All 3 runs must pass for test to pass});
// Bad: Test behaves differently on retrytest('bad retry', async ({ page }, testInfo) => { if (testInfo.retry === 0) { // Do something } else { // Do something different - test is inconsistent! }});// Good: Consistent behaviortest('good retry', async ({ page }, testInfo) => { if (testInfo.retry > 0) { // Only add debugging or longer waits await page.waitForLoadState('networkidle'); } // Same test logic regardless of retry});