Hero Background

Next-Gen App & Browser Testing Cloud

Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Next-Gen App & Browser Testing Cloud

How to Avoid Flaky Tests?

You avoid flaky tests by writing them to be deterministic and self-contained: isolate every test with its own fresh data, replace fixed sleeps with explicit condition-based waits, select elements with stable locators such as data-testid, mock external services, and run tests in consistent, parallel-safe environments. Flakiness is prevented by design, not patched after the fact, so the practices below all focus on writing tests that never go flaky in the first place.

This guide covers preventing flakiness before it starts. If a test is already failing intermittently, see How to Fix Flaky Tests? for the diagnose-and-repair workflow.

9 Practices That Prevent Flaky Tests

Each practice below maps to a specific root cause of flakiness. Apply them as a checklist while writing or reviewing tests:

  • Keep every test isolated with no shared state and no reliance on execution order.
  • Use deterministic, controlled data generated per run instead of hardcoded shared datasets.
  • Choose stable element locators that survive UI changes.
  • Synchronize with explicit waits, never fixed sleeps.
  • Standardize the environment so every run is reproducible.
  • Design for parallel and order independence from the start.
  • Mock or stub external services to remove network variability.
  • Keep tests small and atomic, asserting one behavior each.
  • Monitor for flakiness early and quarantine suspects instead of ignoring them.

What Makes a Test Flaky (and Why Prevention Beats Fixing)

A flaky test is one that passes and fails inconsistently against the same code, with no change to the application under test. The usual root causes are timing and synchronization gaps, shared state between tests, brittle locators, unstable environments, external dependencies, and order or parallel-execution conflicts. Because the failure is non-deterministic, chasing it after the fact is slow and frustrating, while preventing it at design time is cheap and permanent.

Prevention wins because every flaky failure also erodes trust: once a suite cries wolf, teams start re-running red builds reflexively and real regressions slip through. The categories below each neutralize one root cause. If you want to surface flakiness that already exists in a suite, pair this with How to Detect Flaky Tests?.

Keep Every Test Isolated and Independent

Shared state is the most common reason a test that passes alone fails in a suite. Make each test fully self-contained so it neither depends on nor leaks into any other test.

  • No shared state: A test must never rely on data, login sessions, or side effects left behind by a previous test, and must never assume a particular execution order.
  • Idempotent setup and teardown: Each test creates the records it needs and cleans them up afterwards, so re-running it produces the same result every time.
  • Reset state between tests: Reset or seed the database, clear caches, and use unique records per run so two tests can never collide on the same row or fixture.
  • Fresh context per test: Use per-test fixtures such as a beforeEach seed in Cypress or a fresh browser context in Playwright so cookies and storage start clean.

Use Deterministic, Controlled Test Data

Tests that read live or shared data fail the moment that data changes. Generate exactly the inputs each test needs and pin everything that would otherwise vary between runs.

  • Build data with factories: Create test objects through factories or builders rather than relying on hardcoded shared datasets that drift over time.
  • Seed randomness: If a test uses random values, seed the generator so the same inputs are reproduced on every run.
  • Control time: Freeze or mock the system clock and the Date object so time-dependent logic does not flip at midnight, month boundaries, or daylight-saving changes.
  • Pin locale and timezone: Fix locale, timezone, and currency so formatting assertions behave the same on every machine and in every region.
  • Prefer static snapshots: Test against a controlled snapshot rather than live production data that other people can mutate underneath you.

Choose Stable, Resilient Element Locators

Locators tied to fragile parts of the DOM break with every cosmetic UI change and produce intermittent failures. Anchor selectors to attributes that exist specifically for testing.

  • Prefer test-stable attributes: Use data-testid, ARIA roles, or accessibility labels instead of CSS classes, visible text, or position-based selectors that shift when the design changes.
  • Avoid absolute XPath: Long structural XPath paths break the instant a wrapper element is added; base any XPath on a stable attribute, not on absolute position.
  • Centralize locators: Keep selectors in one place using the Page Object Model so a UI change is a single edit rather than a hunt across dozens of tests.

For deeper guidance see How Do I Locate Elements on a Web Page and Interact with Them? and What Is a Page Object Model?.

Synchronize Properly — Never Use Fixed Sleeps

Hardcoded sleeps are the single biggest source of timing flakiness: too short and the test fails when the app is slow, too long and the suite crawls. Wait for the actual condition instead of a guessed duration.

  • Replace sleeps with explicit waits: Swap Thread.sleep or fixed setTimeout calls for condition-based waits that poll until an element is visible, the network is idle, or a state has changed.
  • Use built-in auto-waiting correctly: Lean on the retry-ability and auto-waiting in Playwright and Cypress rather than adding your own delays on top, which usually reintroduces races.
  • Wait for completion, not appearance: Synchronize on AJAX calls finishing, animations settling, and background jobs completing before asserting, so you never read a half-rendered page.

Related reading: What Are the Different Types of Waits Available in WebDriver? and Why Are Selenium Tests Running Slow or Timing Out?.

Control and Standardize the Test Environment

A test that passes locally but fails in CI is usually a victim of environment drift rather than a code bug. Make the runtime identical everywhere the suite executes.

  • Containerize the runtime: Run tests inside a consistent, version-pinned container such as Docker so every machine executes against the same OS libraries and dependencies.
  • Pin versions: Lock browser, driver, and runtime versions so an automatic update cannot quietly change behavior mid-sprint.
  • Stabilize the network: Control or stub network conditions so latency spikes and outages cannot intermittently break unrelated assertions.

Running tests on a consistent cloud grid of real browsers and operating systems, such as the TestMu AI Real Device Cloud, removes the drift between local, CI, and teammate machines that produces a whole class of works-on-my-machine flakiness.

Design for Parallel Execution and Order Independence

Tests that only pass in a fixed sequence become flaky the moment you parallelize them to speed up the suite. Build order and concurrency safety in from the beginning.

  • Any order, any worker: A test must pass whether it runs first, last, alone, or concurrently with hundreds of others, with no shared files, ports, fixtures, or accounts.
  • Isolate per worker: Give each parallel worker its own data namespace and credentials so two workers never write to the same record at once.
  • Avoid global singletons: Shared in-memory caches, static counters, and global config are race conditions waiting to happen under concurrency.

Executing tests in parallel across a cloud grid also surfaces order and concurrency flakiness early instead of in production CI. See How Test Infrastructure Supports Parallel Testing? for the underlying setup.

Mock or Stub External Services

Every real third-party call introduces network variability and uptime you do not control, which shows up as random failures unrelated to your code.

  • Stub third-party APIs: Replace payment gateways, email and SMS providers, and external APIs with controlled mocks that return deterministic responses.
  • Simulate failure paths: Mocks let you reproduce timeouts and error codes on demand, so error handling is tested deterministically instead of by luck.
  • Validate the real integration separately: Keep a small, dedicated set of contract or integration tests to confirm the live service still matches your mocks, away from the main flow.

Keep Tests Small, Atomic, and Focused

Long, multi-step scenarios give flakiness more places to hide: any one of a dozen steps can intermittently break and the failure tells you little.

  • One behavior per test: Assert a single outcome so a failure points directly at the cause instead of forcing you to bisect a long flow.
  • Shorten the chain: Prefer many small, fast tests over sprawling end-to-end journeys where each extra step adds another opportunity to flake.
  • Push logic down the pyramid: Cover detail in fast unit and API tests and reserve UI tests for the few critical paths, reducing the surface area exposed to timing and rendering issues.

Monitor for Flakiness Early and Continuously

Even disciplined suites accumulate new flakiness as the app and team grow. Catch it the moment it appears rather than after it has trained everyone to ignore red builds.

  • Track pass and fail history: Watch per-test stability trends so a newly unstable test stands out before it spreads doubt across the suite.
  • Quarantine, do not silently disable: Move a suspected flaky test into a tracked quarantine with an owner and a fix deadline instead of quietly skipping it forever.
  • Rerun new tests: Exercise freshly added tests several times before trusting them, so latent non-determinism is caught at review time.

Test Intelligence and flaky-test analytics assign each test a flaky score so instability is flagged automatically as soon as it appears. For more, see How Test Observability Reduces Flaky Tests?.

Frequently Asked Questions

What is the main cause of flaky tests?

Most flakiness traces back to timing and synchronization issues and to shared state between tests. The remainder comes from brittle locators, unstable environments, external dependencies, and order or parallel-execution problems. Each of these has a corresponding preventive practice you can apply while writing the test.

How is avoiding flaky tests different from fixing them?

Avoiding flakiness means applying preventive design practices while you write tests so they stay deterministic from the start. Fixing flakiness means diagnosing and repairing tests that already fail intermittently. This guide covers prevention; for repairing an existing flaky test, see How to Fix Flaky Tests?.

Do retries prevent flaky tests?

No. Retries hide flakiness rather than prevent it. A test that only passes on the second or third attempt is still flaky and still masks real regressions. Use retries only as a temporary safety net while you remove the underlying root cause through isolation, deterministic data, and proper synchronization.

Can flaky tests be eliminated completely?

You can get very close. Deterministic design, strict test isolation, controlled data, and consistent environments remove almost all flakiness by construction. Continuous monitoring then catches any new instability early before it spreads, so practical zero-flakiness is achievable and sustainable.

How do I make tests parallel-safe?

Remove all shared state, give each worker its own isolated data namespace, accounts, files, and ports, and never depend on the order in which tests run. A parallel-safe test passes whether it runs first, last, alone, or concurrently with hundreds of others.

Does cloud testing reduce flaky tests?

Yes. Running tests on consistent cloud browsers and operating systems eliminates the works-on-my-machine environment drift between local, CI, and teammate machines, which is a major source of environmental flakiness. Scaled parallel execution also surfaces order and concurrency issues early instead of in production CI.

Related Questions

Test Your Website on 3000+ Browsers

Get 100 minutes of automation test minutes FREE!!

Test Now...

KaneAI - Testing Assistant

World’s first AI-Native E2E testing agent.

...

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests