Handling Asynchronous Network Requests with cy.intercept()
Learn how to eliminate flaky tests in Cypress using cy.intercept() to stub network requests, handle asynchronous responses with aliases, and simulate server errors.
01 Jul 2026, 10:00 UTC

Solving Flaky Tests with Network Aliasing
The most common cause of flakiness in end-to-end tests is the "race condition": the test attempts to assert that an element exists before the API request that populates that element has finished. Using arbitrary timeouts like cy.wait(2000) is an anti-pattern because it slows down the suite and still fails if the network is unexpectedly slow.
The solution is cy.intercept(). This command allows you to monitor, stub, or modify network requests. By assigning an alias to a request, you can tell Cypress to pause the test execution until the specific network response returns, ensuring the DOM is updated before assertions run.
Implementing the Intercept-Wait Pattern
To use cy.intercept() effectively, you must register the intercept before the action that triggers the request. If you call cy.visit() or click a button before defining the intercept, the request may fire and finish before Cypress starts listening for it.
Worked Example: Stubbing a User Profile
In this scenario, we want to test how the UI handles a specific set of user data without relying on a live database. We will use a fixture (a static JSON file) to provide a deterministic response.
// cypress/e2e/profile.cy.js
describe('User Profile Page', () => {
it('should display user data from the API', () => {
// 1. Register the intercept and assign an alias '@getUser'
// This matches a GET request to /api/user/123
cy.intercept('GET', '/api/user/123', { fixture: 'user.json' }).as('getUser');
// 2. Trigger the request
cy.visit('/profile/123');
// 3. Wait explicitly for the aliased request to complete
cy.wait('@getUser');
// 4. Assert the UI reflects the stubbed data
cy.get('[data-testid="username"]').should('contain', 'Test User');
});
});
Testing Error States with Route Handlers
Stubbing isn't just for "happy paths." You can use a routeHandler function to simulate server failures (like 500 Internal Server Error) to verify your application's error handling logic without needing to manually crash a backend server.
cy.intercept('POST', '/api/settings', (req) => {
req.reply({
statusCode: 500,
body: { error: 'Internal Server Error' },
headers: { 'ct-type': 'application/json' }
});
}).as('saveSettings');
cy.get('#save-button').click();
cy.wait('@saveSettings');
cy.get('.error-toast').should('be.visible').and('contain', 'Internal Server Error');
Matching Strategies and Precision
Cypress provides several ways to match requests. Choosing the right one prevents "over-matching," where one intercept accidentally catches requests intended for another.
| Method | Example | Best Use Case |
|---|---|---|
| String | '/api/users' |
Exact URL matches. |
| Glob | '**/api/users/*' |
URLs with dynamic IDs or varying domains. |
| RegExp | /api/users\/\d+/ |
Complex patterns (e.g., ensuring an ID is numeric). |
Limitations and Common Pitfalls
The "Missing Intercept" Trap
If you define cy.intercept() after cy.visit(), the request often bypasses the stub. Always place your intercepts at the top of the it block or within a beforeEach hook.
Fixture Drift
Stubbing creates a "contract gap." If the backend API changes a field name from userName to username, your stubbed tests will still pass because they use the old user.json file, but the production app will break. To mitigate this, maintain a small set of "smoke tests" that run against a real staging environment without stubs.
Scope of Interception
cy.intercept() only monitors traffic originating from the browser under test. It cannot intercept requests made by Cypress plugins running in Node.js or requests made by external services calling your backend.
Verification Checklist
- Check the Command Log: In the Cypress Test Runner, look for the
(XHR/Fetch)entry. If it is labeled with your alias (e.g.,@getUser), the intercept worked. - Verify Status Codes: Use
cy.wait('@alias').its('response.statusCode').should('eq', 200)to ensure the server (or stub) returned the expected code. - Test the Timeout: If
cy.wait()times out, it means the request was never fired. Check if the trigger (click/visit) actually occurred.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.