Cypress interview question
How do you diagnose and fix flaky Cypress tests?
Short answer
Reproduce and classify the failure, then fix the cause: understand which commands retry (queries and assertions) and which don't (actions, .then), avoid detached-DOM errors by re-querying after re-renders, synchronise on network aliases not cy.wait(ms), isolate data, and use test retries only as a signal, not a cure.
Answer
Flakiness is a symptom; my job is to find which category it belongs to and fix the cause. My process:
1. Reproduce and gather evidence
- Run the spec repeatedly in headless CI-like conditions (
cypress run --spec ... --browser chrome), in a loop or with a burn-in job. - Collect screenshots, videos and the command log. Cypress Cloud’s flake detection, or my own reports, show which tests pass on retry.
- Check whether it fails only in CI (resources, timing, data) or also locally.
2. Classify the failure
Timing and retry-ability misunderstandings
Cypress retries queries (cy.get, .find, .contains) together with the assertions that follow them. It does not retry actions (.click, .type) or .then() callbacks. A common flaky pattern:
// Flaky: .then() runs once, possibly before the list has updated
cy.get('[data-cy=row]').then(($rows) => {
expect($rows).to.have.length(5);
});
// Stable: the query and assertion retry together until timeout
cy.get('[data-cy=row]').should('have.length', 5);
Detached DOM
“Element is detached from the DOM” means Cypress held a reference to an element that the framework then re-rendered. Since Cypress 12, queries re-run automatically through the chain, but actions end that protection. Chaining further off an action is the usual culprit:
// Risky: the element yielded by .type() may be replaced on re-render
cy.get('[data-cy=search]').type('lamp').parent().find('button').click();
// Better: split after actions and re-query
cy.get('[data-cy=search]').type('lamp');
cy.get('[data-cy=search-submit]').click();
Also assert on the state that indicates rendering is complete (a spinner gone, a count updated) before acting.
Network races
Tests that act before data arrives, or assert before the save completes. The fix is to intercept and wait on the alias, not cy.wait(2000):
cy.intercept('PUT', '/api/profile').as('save');
cy.get('[data-cy=save]').click();
cy.wait('@save').its('response.statusCode').should('eq', 200);
cy.contains('Saved').should('be.visible');
Data and order dependence
Tests sharing a user or relying on records created by another spec. Fix with per-test data created through cy.request or cy.task, unique identifiers, and cy.session for login.
Environment
Animations, third-party scripts, under-resourced CI containers (Chrome crashing with out-of-memory), or time-dependent logic. Stub third parties, use cy.clock() for time, and give CI agents adequate memory and shared memory for browsers.
3. Use retries carefully
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
retries: { runMode: 2, openMode: 0 },
e2e: { baseUrl: 'http://localhost:3000' },
});
Retries reduce noise in CI, but a test that only passes on retry is still flaky, and might be flagging a real race condition in the product. I track pass-on-retry rates, quarantine persistent offenders with an owner and a deadline, and fix the root cause rather than raising timeouts.
What interviewers look for
- A systematic process: reproduce, classify, fix, verify.
- Precise understanding of what retries and what doesn’t.
- Treating retries as a detection mechanism, and considering that the flake could be a product bug.
Common mistakes
- Increasing
defaultCommandTimeoutglobally. - Sprinkling
cy.wait(ms)and{ force: true }to make failures disappear.
A strong answer covers
- Retry-ability: queries and assertions retry, actions and .then do not
- Detached DOM: re-query after re-render, split chains after actions
- Synchronise on app state/network aliases instead of fixed waits
- Data isolation and environment causes (shared data, CI resources)
- Test retries/burn-in as detection, not a fix