Understanding the CI Flakiness Problem
Symptoms of Flaky Cypress Tests
- Tests pass locally but fail intermittently in CI
- Tests timeout despite correct selectors
- Race conditions when elements load slowly
- State leaking across test files
Root Causes
- Asynchronous behavior not properly awaited
- CI environments having different CPU/network characteristics
- Improper use of global fixtures or
beforeEach()
leakage - Dependence on hardcoded waits or brittle selectors
Diagnostics and Debugging Strategy
1. Use cypress run --headed --no-exit
in CI
This allows visual inspection of how tests behave in CI versus local:
cypress run --headed --no-exit --browser chrome
2. Enable Detailed Logging
Enable verbose logging using DEBUG=cypress:*
in CI to view timing and retry logic:
DEBUG=cypress:* npx cypress run
3. Check Command Retries and Timeouts
Misconfigured timeouts lead to race conditions. Audit settings in cypress.config.js
:
defaultCommandTimeout: 8000, pageLoadTimeout: 60000
Common Pitfalls in Cypress Test Design
1. Overuse of cy.wait()
Hardcoded waits (e.g., cy.wait(2000)
) are unreliable. Prefer assertions that implicitly wait:
cy.get('.table-row').should('have.length.at.least', 1)
2. Improper Fixture or Session Isolation
Global before
hooks or cookies not reset between tests may cause state bleed. Use cy.session()
or isolate test data.
3. DOM Pollution Across Specs
If DOM is not reset properly between specs, stale elements can cause failed assertions. Ensure cy.visit()
is called in every test file.
Step-by-Step Fix Strategy
1. Refactor Waits Into Assertions
Replace arbitrary delays with condition-based assertions:
cy.get('#submit-button').should('be.visible').click()
2. Isolate State Per Test
Reset state in beforeEach()
and avoid reusing data between specs:
beforeEach(() => { cy.clearCookies(); cy.visit('/login'); })
3. Use cy.intercept()
for API Synchronization
Intercept API calls instead of waiting on UI indicators:
cy.intercept('POST', '/api/orders').as('postOrder') cy.get('button.submit').click() cy.wait('@postOrder')
4. Avoid Cross-Test Dependencies
Each test should be atomic and independent. Do not rely on state created by previous tests.
5. Run Tests Headlessly With Retries
Use built-in retry logic to stabilize CI runs:
{ retries: { runMode: 2, openMode: 0 } }
Best Practices for Enterprise Cypress Projects
- Use unique data in tests to avoid collisions
- Set CI resolution to 1280x720 to match developer machines
- Configure
baseUrl
andenv
via environment-specific config files - Enable video recording and screenshots on failure for root cause analysis
- Integrate with GitHub Actions or Jenkins using proper containers and dependencies
Conclusion
Cypress is a powerful tool for testing web applications, but achieving consistent, reliable results at scale requires deliberate engineering effort. Inconsistent test behavior—particularly in CI environments—can almost always be traced to shared state, timing mismatches, or poor test design. By focusing on atomic test principles, API synchronization, and retry strategies, development teams can transform flaky Cypress suites into dependable validation pipelines that accelerate, rather than hinder, delivery cycles.
FAQs
1. Why do Cypress tests pass locally but fail in CI?
CI environments often have different performance characteristics. Network speed, CPU usage, or screen resolution can affect timing-sensitive tests.
2. Is cy.wait()
ever acceptable?
Only in specific cases where the delay is deterministic, such as animations. Prefer cy.intercept()
or assertions when possible.
3. How can I handle flaky tests that still sometimes fail?
Use Cypress retry logic and refactor your tests to isolate state and synchronize API behavior. Investigate with video playback.
4. Can Cypress handle parallel execution in CI?
Yes. Cypress Dashboard offers parallelization and load balancing across CI runners, improving execution time and reliability.
5. What tools can help debug Cypress tests?
Use the built-in time-travel debugger, DevTools console, video recordings, and DEBUG=cypress:*
logging for deep diagnostics.