How to Fix Flaky Tests: Stop Using networkidle and What to Do Instead
Stop waiting for your network to go quiet. waitForLoadState('networkidle') is the single most common cause of flaky Playwright tests — and teams keep using it because it seems intuitive.
This guide covers the 7 root causes of flaky tests and gives you copy-paste patterns to fix each one permanently.
Contents
The networkidle Anti-Pattern
The problem: networkidle waits until there are no network connections for 500ms. But modern SPAs have background polling, analytics pings, WebSocket heartbeats, and lazy-loaded resources that keep the network active. Your test becomes a coin flip.
Bad Pattern
await page.goto('https://app.example.com/dashboard');
await page.waitForLoadState('networkidle'); // Flaky!
await page.click('#submit-button');
Good Pattern
await page.goto('https://app.example.com/dashboard');
await expect(page.locator('#submit-button')).toBeVisible(); // Stable!
await page.click('#submit-button');
The 7 Root Causes of Flaky Tests
1. Timing Issues (networkidle, sleep, hardcoded waits)
Fix: Replace all implicit waits with explicit element assertions. Use expect(locator).toBeVisible(), .toHaveText(), or .toBeEnabled().
Fix: Each test creates its own data via API. Use beforeEach to set up isolated state. Never depend on data created by another test.
3. Network Dependency
Fix: Mock external APIs with page.route(). Intercept third-party calls and return predictable responses.
4. Environment Inconsistency
Fix: Run tests in Docker containers with identical configurations. Use environment-specific config files, not hardcoded URLs.
5. Animation and Transition Delays
Fix: Disable CSS animations in test mode. Add * { animation: none !important; transition: none !important; } or use Playwright’s reducedMotion option.
6. Order-Dependent Tests
Fix: Each test must be independently runnable. If test B fails when run alone but passes after test A, you have an order dependency.
7. External Service Calls
Fix: Stub all third-party integrations (payment gateways, email services, analytics). Use page.route('**/api/stripe/**', ...) to intercept and mock.
The Flaky Test Audit Checklist
- Search your codebase for
networkidle— replace every instance - Search for
waitForTimeoutandsleep— replace with element assertions - Run each test in isolation — flag any that fail when run alone
- Check for shared database state between tests
- Verify all external API calls are mocked in CI
- Add retry logic only as a last resort, not as a fix
