Repository navigation
How Can I Make Cypress Tests More Reliable When API Responses Are Unpredictable? #35253
|
I'm exploring ways to improve the reliability of end-to-end tests using Cypress, especially when an application depends on API responses that may change depending on the test environment. Sometimes tests pass locally but fail in CI because of timing differences, delayed API responses, or inconsistent test data. I'd like to understand how experienced Cypress users handle these situations. Is cy.intercept() the best approach for controlling API responses during E2E tests? How do you avoid flaky tests caused by network delays? Do you prefer using fixtures or dynamic API responses for test data? What practices help keep Cypress tests stable across local and CI environments? I'm also interested in broader software testing and quality assurance practices, which are areas covered by IAMQA. I'd appreciate any practical advice or examples from your experience. |
Replies: 2 comments
|
Short version: control the network for most tests, keep a few tests against the real API, and never wait on time, only on state. 1. Use cy.intercept('GET', '/api/orders*', { fixture: 'orders.json' }).as('orders')
cy.visit('/orders')
cy.wait('@orders')
cy.get('[data-cy=order-row]').should('have.length', 3)Most UI tests don't need the real backend. Stubbing removes the "different data in CI" and "slow API" problems in one go. For a few critical flows, keep end-to-end tests against a real (seeded) API, so the stubs can't hide a contract change. 2. Wait on aliases and assertions, not 3. Fixtures or dynamic responses? Fixtures for the stable shape of the data, plus cy.intercept('GET', '/api/orders*', (req) => {
req.reply({ fixture: 'orders.json', delay: 800 }) // also test the loading state
})
cy.intercept('POST', '/api/orders', { statusCode: 500, body: { error: 'boom' } }) // and the error path4. Own the test data when you do hit a real API. Create what the test needs in 5. CI-specific things.
The Cypress docs on network requests and best practices cover these patterns in more depth. |
|
For flaky tests caused by unpredictable API responses, cy.intercept() with fixtures is generally the right move for anything where you want deterministic behavior — stub the network at the boundary and your tests stop caring about backend timing. For tests that must hit real APIs, use cy.intercept() as a spy plus generous but explicit waits (cy.wait('@alias')) rather than arbitrary cy.wait(ms) calls, and make sure your CI environment seeds consistent test data before the suite runs. Separating your tests into 'stubbed' and 'live API' suites also helps you pinpoint whether a failure is a frontend logic issue or a backend/data problem. I'm building QAOnFire (https://qaonfire.dev?utm_source=github&utm_medium=community), a GitHub App that runs AI-powered QA review on pull requests and can catch patterns likely to cause flakiness before they merge — might be worth a look. |
Short version: control the network for most tests, keep a few tests against the real API, and never wait on time, only on state.
1. Use
cy.intercept()to make responses deterministic.Most UI tests don't need the real backend. Stubbing removes the "different data in CI" and "slow API" problems in one go. For a few critical flows, keep end-to-end tests against a real (seeded) API, so the stubs can't hide a contract change.
2. Wait on aliases and assertions, not
cy.wait(ms).cy.wait('@alias')waits for the actual request, …