Choosing Between cy.intercept() and cy.request() in Cypress E2E Tests
Decide when to stub API calls with cy.intercept() or hit the real endpoint with cy.request() in Cypress. A practical guide compares speed, reliability, maintenance, and contract validation, then shows concrete test snippets and how to measure trade‑offs.
17 Mar 2026, 09:13 UTC

Problem & Takeaway
When writing end‑to‑end tests with Cypress, you frequently need to decide whether to stub API responses using cy.intercept() or to make real HTTP calls with cy.request(). The choice impacts test speed, reliability, and the level of integration validation you get. This guide helps you pick the right approach for your test suite.
Decision Context
Before you choose, ask yourself:
- Do I need to validate the actual API contract (status codes, payload shape) or is a mock sufficient?
- How critical is test speed and isolation from external services?
- What is the maintenance cost of keeping stub definitions up to date?
- Will the API call consume rate limits or incur costs?
- Do I have the necessary CORS or authentication setup to hit the real endpoint from the test environment?
Option Comparison
| Aspect | cy.intercept() (Stub) | cy.request() (Real API) |
|---|---|---|
| Purpose | Mock server responses, control latency, isolate UI | Execute real HTTP request, validate contract |
| Speed | Fast – no network round trip | Slower – network latency and server response time |
| Reliability | Deterministic – no external flakiness | Prone to network/API failures |
| Maintenance | Requires keeping stub definitions in sync with API changes | Minimal – uses live endpoint |
| Integration Coverage | Misses real API errors (wrong status, schema) | Captures real errors, validates contract |
| Cost & Rate Limits | No external cost | May hit rate limits or incur charges |
| Setup Complexity | Simple – no auth required if same origin | May need auth headers or CORS configuration |
Trade‑Off Summary
- Use
cy.intercept()when: you want fast, isolated UI tests, or you are working against a stable mock API that mimics production responses. - Use
cy.request()when: you need to ensure the real API behaves correctly, validate contract changes, or catch integration bugs that stubs would hide. - Often a hybrid approach works best: stub most endpoints but hit critical ones (e.g., authentication, data creation) with
cy.request()to maintain contract confidence.
Concrete Implementation
Stubbed Test Example
Below is a test that mocks a GET /api/user call and verifies the UI displays the mocked data.
// cypress/integration/user_spec.js
describe('User Dashboard', () => {
it('shows mocked user name', () => {
// 1. Stub the API response
cy.intercept('GET', '/api/user', {
statusCode: 200,
body: { name: 'Mocked User', id: 42 }
}).as('getUser');
// 2. Visit the page that triggers the request
cy.visit('/dashboard');
// 3. Wait for the stubbed request to complete
cy.wait('@getUser');
// 4. Assert UI reflects the mocked data
cy.get('.user-name').should('contain', 'Mocked User');
});
});
Real‑API Test Example
In this variant we call the actual endpoint, validate the response, and then confirm the UI shows the real data.
// cypress/integration/user_real_spec.js
describe('User Dashboard (real API)', () => {
it('shows real user name', () => {
// 1. Make a real request
cy.request({
method: 'GET',
url: '/api/user',
// If the API requires auth, include headers or cookies here
// headers: { 'Authorization': 'Bearer token' }
}).then((resp) => {
// 2. Validate the response contract
expect(resp.status).to.eq(200);
expect(resp.body).to.have.property('name');
// 3. Visit the page that consumes the API
cy.visit('/dashboard');
// 4. Assert UI matches the real data
cy.get('.user-name').should('contain', resp.body.name);
});
});
});
Execution Time & Flakiness Check
To compare the two approaches on your CI pipeline, add a simple timer:
const start = Date.now();
cy.visit('/dashboard');
// ... test steps ...
cy.then(() => {
const duration = Date.now() - start;
cy.log('Test duration:', duration, 'ms');
});
Collect the logged durations for both test variants over several runs. A consistent 10–20 % slower runtime for the real‑API test usually indicates network overhead. If you notice a higher flake rate (e.g., 1–2 % of runs failing due to timeouts or 5xx responses), that signals external dependency issues.
Limitations & Practical Checks
- Stubbing can hide real errors: If the API changes its status code or payload shape, the stub will continue to return the old data, masking integration bugs.
- Real requests may fail on CI: Ensure the test environment has network access, correct CORS settings, and any required authentication tokens. Use the
cy.request()command’sfailOnStatusCodeoption to control failure handling. - Verify stubs are hit: After
cy.wait('@alias'), check the command log to confirm the request was intercepted and not passed to the server. - Validate contract changes: Periodically run a subset of real‑API tests against a staging environment to catch schema drift, even if most tests use stubs.
Practical Checklist Before Hooking Up
- Identify critical endpoints that must hit the real API.
- Define stubs for all other endpoints, keeping them in a shared
cypress/fixturesfile. - Set up a CI job that runs both stubbed and real tests in parallel.
- Collect metrics: duration, flake rate, API error logs.
- Review metrics quarterly and adjust the stub/real split accordingly.
Conclusion
Choosing between cy.intercept() and cy.request() is a trade‑off between speed, isolation, and contract confidence. Use stubs for fast, deterministic UI coverage; use real requests for integration validation. A hybrid strategy, combined with regular metric monitoring, gives you the best of both worlds.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.