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().

2. Shared State Between Tests

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 waitForTimeout and sleep — 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

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.