Skip to content

Cypress Network Testing

Network testing is one of Cypress's biggest advantages over older tools. This lesson introduces why controlling the network layer matters before diving deeper into cy.intercept(), stubs, spies, and API testing in the following lessons.

Why Network Testing Matters

Most modern web applications are heavily driven by network requests: initial data loads, form submissions, search-as-you-type, and more. Testing the UI in isolation from real backend variability, latency, flaky third-party services, changing data, produces faster, more deterministic, and more focused tests.

Cypress runs in the browser, giving it native access to intercept, inspect, and modify outgoing requests and incoming responses without a separate proxy tool, something that is comparatively awkward to achieve with external WebDriver-based tools.

cy.intercept('GET', '/api/orders', { fixture: 'orders.json' }).as('getOrders');
cy.visit('/orders');
cy.wait('@getOrders');
cy.get('[data-cy="order-row"]').should('have.length', 3);

This test is fully deterministic: three orders, from a known fixture, regardless of what a real backend might currently contain.

The Network Testing Toolkit

cy.intercept(method, url, response)   // stub or observe a request
cy.wait('@alias')                     // wait for a specific request
cy.request(method, url, body)         // make a direct HTTP request
cy.spy() / cy.stub()                  // lower-level function mocking
  • cy.intercept() is the primary tool for controlling network requests in E2E tests.
  • cy.request() bypasses the browser entirely for direct HTTP calls, often used for setup or API testing.
  • cy.spy()/cy.stub() work at the JavaScript function level, useful in component testing.
  • Combining a stubbed response with cy.wait('@alias') keeps a test deterministic and fast.

Network Testing Toolkit Cheat Sheet

Choosing the right network testing tool for a given scenario.

Goal Tool
Control a browser-triggered request's response cy.intercept()
Wait for a specific request to complete cy.wait('@alias')
Make a direct API call outside the UI cy.request()
Mock a JavaScript function/callback cy.stub()
Observe calls without changing behavior cy.spy()

Stubbing vs. Letting Requests Hit a Real Backend

Not every test should stub every request. Critical end-to-end smoke tests benefit from hitting a real backend to confirm true integration; most feature-level UI tests benefit far more from deterministic, stubbed responses that isolate frontend behavior.

  • Use real requests for a small number of true, full-stack smoke tests.
  • Use stubbed requests for the majority of feature-focused UI tests.
  • Reserve full-stack integration coverage for critical, high-value user flows.

Testing Error and Edge-Case States Easily

Reproducing a 500 error, a slow response, or an empty dataset against a real backend can be difficult or impossible on demand. Stubbing responses makes these edge cases trivial to test consistently, every single run.

cy.intercept('GET', '/api/orders', {
  statusCode: 500,
  body: { message: 'Internal server error' },
}).as('getOrdersError');

cy.visit('/orders');
cy.wait('@getOrdersError');
cy.get('[data-cy="orders-error"]').should('be.visible');

Common Mistakes

  • Stubbing every single request in every test, losing all real integration coverage entirely.
  • Never stubbing anything, leaving tests slow and vulnerable to unrelated backend issues.
  • Testing error states only manually instead of building them into the automated suite via stubbed responses.
  • Confusing cy.request() (a direct HTTP call from the test) with cy.intercept() (controlling the app's own requests).

Key Takeaways

  • Cypress can observe and control network requests natively, without an external proxy tool.
  • A healthy suite mixes a few real, full-stack tests with many stubbed, deterministic feature tests.
  • Stubbing makes error states and edge cases trivial and consistent to test.
  • cy.intercept(), cy.request(), cy.spy(), and cy.stub() each serve a distinct purpose.

Pro Tip

Deliberately write at least one stubbed test per feature specifically for its error state (500 responses, empty data, slow responses), these are exactly the scenarios that are hardest to reproduce manually and easiest to silently regress without any dedicated coverage.