Taming Flaky Tests
Replace sleeps and races with deterministic waits, isolation, and quarantine.
- flakiness
- e2e
- reliability
Problem
A test passes locally and fails one run in ten in CI. Flaky tests erode trust until the whole suite gets ignored — worse than having no suite at all.
Solution
Most flakiness is a hidden race: the test asserts before the app settles. Wait for the observable condition, never a fixed timeout. Control the clock, network, and randomness so each run is deterministic, and make every test set up and tear down its own data.
When you cannot fix a flaky test immediately, quarantine it (and track it) rather than retrying blindly — silent retries hide real bugs.
Snippet
// ❌ Arbitrary timing — slow and still racy
await page.waitForTimeout(2000);
expect(await page.locator('.toast').count()).toBe(1);
// ✅ Web-first, auto-retrying assertion
await expect(page.getByText('Saved')).toBeVisible();Pitfalls
- Bumping global retries to hide flakiness — it masks real product bugs and slows the suite.
- Sharing mutable state (DB rows, logged-in session) across tests, creating order dependencies.
- Asserting on timing-sensitive values (animations, "2 seconds ago") without faking the clock.