Prevent flaky tests with Playwright
Source: Dev.to

Flaky tests are technical debt.
Be careful with loops and manual delays in your e2e tests.
Loop .all()
You can leverage locator.all() to get an array of Locator objects for all elements matching the locator’s selector. It’s helpful for checking counts and other associated values.
Avoid naive for loops ❌:
const items = await page.locator('.myselector').all();
for (item in items) { // or for (let i = 0; ... )
// …
}
Such loops can introduce flakiness and improper handling of async locators.
Recommended approaches ✅:
- Use bulk‑action helpers like
allInnerTexts(),allTextContents(),count(), orfilter(). - If you absolutely need a loop, do it like this:
for (const item of items) {
await item.scrollIntoViewIfNeeded();
await expect(item).toBeVisible();
}
Playwright usually provides bulk actions on locators, so explicit loops are often unnecessary.
Timing Violations
Tests can miss their targets due to dynamic DOM changes or pages that aren’t ready yet. Even though Playwright adds some auto‑waiting, it’s better to make assertions explicit.
Don’t rely on implicit waits ❌:
const anchor = await expect(page.locator('#anchor'));
Prefer explicit checks ✅:
await expect(page.locator('#target')).toBeVisible();
await page.locator('#target').scrollIntoViewIfNeeded();
Source: Playwright – Auto‑waiting
Convenient Configuration
Playwright can automatically retry flaky tests:
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: 5,
});
Test Result Categories
- Passed – succeeds on the first attempt
- Flaky – fails initially but passes on a retry
- Failed – fails all attempts
Do not use retries to ignore flaky tests ❌.
Flag flaky tests and fix them ✅.