Skip to content

Fixing Flaky Cypress Tests

A flaky test passes and fails unpredictably without any code change. This lesson catalogs the most common root causes in Cypress suites and how to fix each one properly.

What Makes a Test Flaky?

Flakiness usually comes down to a mismatch between what a test assumes and what actually happens: assuming a fixed amount of time is always enough, assuming a previous test left the app in a particular state, or assuming a network response always arrives in the same order.

Because Cypress already handles a large share of timing issues automatically, most remaining flakiness in a Cypress suite traces back to test design choices rather than the tool itself.

// Flaky: assumes a fixed 1 second is always enough
cy.wait(1000);
cy.get('[data-cy="results"]').should('have.length', 5);

// Stable: waits for the real condition, however long it takes
cy.get('[data-cy="results"]').should('have.length', 5);

Removing the fixed wait here does not remove any real waiting, Cypress's own retry loop already covers it.

Common Root Causes at a Glance

1. Fixed-duration waits instead of assertion-based waits
2. Tests depending on shared state/order
3. Unstubbed, unpredictable network responses
4. Selectors matching multiple or unstable elements
5. Animations/transitions not accounted for
  • Fixed waits guess at timing instead of reacting to real conditions.
  • Order-dependent tests break when run individually, in parallel, or reordered.
  • Real network calls introduce timing and data variability outside your control.
  • Ambiguous selectors can match a different element than intended depending on page state.

Flaky Test Root Cause Cheat Sheet

Common symptoms mapped to their likely root cause and fix.

Symptom Likely Cause Fix
Passes locally, fails in CI Timing/environment differences Assertion-based waits, avoid fixed delays
Fails only when run with others Shared state between tests Reset state in beforeEach, avoid test order dependence
Fails intermittently on network calls Real, unstubbed network variability Use cy.intercept() to control responses
Clicks the wrong element sometimes Ambiguous selector Use a unique data-cy attribute
Fails during animations Assertion runs mid-transition Assert on the final, settled state

Isolating State Between Tests

Tests that depend on data or state left behind by a previous test are a leading cause of flakiness, especially once tests run in a different order or in parallel across CI machines. Resetting relevant state in beforeEach(), via cy.request() to a reset endpoint, a database seed task, or cy.session(), keeps each test self-contained.

beforeEach(() => {
  cy.request('POST', '/test/reset-db');
  cy.visit('/dashboard');
});

A dedicated test-only reset endpoint (guarded so it never runs in production) is a common, effective pattern.

Controlling Network Variability with cy.intercept()

Real network calls introduce variability, latency, occasional errors, backend data changes, that has nothing to do with the frontend behavior actually under test. Stubbing responses with cy.intercept() (covered in depth in a later module) removes this entire category of flakiness for UI-focused tests.

Measuring Flakiness Instead of Guessing

Before attempting a fix, run the suspected test repeatedly (Cypress Cloud's test analytics, or a simple local loop with --spec and a shell loop, both work) to confirm the failure is genuinely intermittent and to gather multiple failure snapshots for comparison, rather than reacting to a single anecdotal failure.

Common Mistakes

  • Reacting to a single flaky failure by immediately adding a longer fixed wait instead of investigating.
  • Writing tests that rely on execution order or leftover state from a previous test.
  • Testing against live, real network responses when the test's actual concern is UI behavior, not the backend itself.
  • Never re-running a suspected flaky test enough times to confirm the failure is genuinely intermittent.

Key Takeaways

  • Most Cypress flakiness traces back to test design, not the tool's own waiting mechanics.
  • Resetting state per test and avoiding order dependence removes a major class of flaky failures.
  • Stubbing network responses with cy.intercept() removes backend-driven variability from UI tests.
  • Confirm flakiness is real (via repeated runs) before attempting a fix, rather than guessing from one failure.

Pro Tip

Keep a lightweight log of flaky test fixes and their actual root cause. Over time this becomes one of the most useful documents a team has, patterns like "ambiguous selector" or "missing state reset" tend to repeat, and the log helps new contributors recognize them immediately.