Answer
Retry Logic in Playwright
Global Retry Configuration
TypeScript
// playwright.config.ts
export default defineConfig({
retries: 2, // Retry all tests up to 2 times
// Or conditionally — more retries in CI
retries: process.env.CI ? 2 : 0,
});
With retries: 2, a failing test gets 3 attempts total (1 original + 2 retries).
CLI Override
Bash
npx playwright test --retries=3
Detect Retry Attempt in Test
TypeScript
import { test, expect } from '@playwright/test';
test('potentially flaky test', async ({ page }, testInfo) => {
console.log(`Attempt ${testInfo.retry + 1} of ${testInfo.retries + 1}`);
if (testInfo.retry > 0) {
// On retry — take a fresh screenshot for debugging
await page.screenshot({ path: `retry-${testInfo.retry}-start.png` });
}
await page.goto('/products');
await expect(page.locator('.product-list')).toBeVisible();
});
Skip Retries for Specific Tests
TypeScript
// This test should never be retried (e.g., destructive operations)
test('delete all records', async ({ page }) => {
test.fixme(!!testInfo.retry, 'Do not retry destructive tests');
// ...
});
Retry Only on Network Failures
TypeScript
test.beforeEach(async ({ page }, testInfo) => {
if (testInfo.retry) {
// Clear state before retry
await page.context().clearCookies();
}
});
Per-Test Retry
TypeScript
test('flaky API test', {
retries: 3, // Override global setting for this test
}, async ({ page }) => {
// ...
});
onRetry Hook (Custom Reporting)
TypeScript
// playwright.config.ts
export default defineConfig({
reporter: [
['html'],
['./custom-reporter.ts'],
],
retries: 2,
});
// custom-reporter.ts
class CustomReporter {
onTestEnd(test: TestCase, result: TestResult) {
if (result.status === 'passed' && result.retry > 0) {
console.log(`⚠️ Test passed on retry ${result.retry}: ${test.title}`);
}
}
}
Difference Between Retries and Flaky Tests
- ✓Retries help with real intermittent failures (network timeout, race conditions)
- ✓Retries should NOT be used to hide test logic bugs
- ✓Tests that consistently need retries indicate a flaky test — investigate root cause
Retry + Trace for Debugging
TypeScript
// playwright.config.ts
export default defineConfig({
retries: 2,
use: {
trace: 'on-first-retry', // Capture trace only on first retry
},
});
This captures a full trace on the retry attempt but not on the original run — minimizing overhead while ensuring debugging data is available when failures occur.
