Skip to content

cy.intercept() in Cypress

cy.intercept() is Cypress's core network control command, replacing the older cy.route(). This lesson covers its full range of capabilities: stubbing, spying, dynamic handlers, and simulating delays or errors.

The Shape of cy.intercept()

cy.intercept(method, url, response) matches outgoing requests by HTTP method and URL pattern. If a response is provided, Cypress stubs the request entirely, the real network is never hit. If no response is provided, Cypress simply observes ("spies on") the real request as it passes through.

// Stub: never hits the real network
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as('getProducts');

// Spy only: request still goes to the real backend
cy.intercept('POST', '/api/analytics').as('trackEvent');

Both forms let you cy.wait('@alias') and assert on the request, the difference is only whether the response is faked.

cy.intercept() Signatures

cy.intercept(url)
cy.intercept(method, url)
cy.intercept(method, url, response)
cy.intercept(routeMatcher, response)
cy.intercept(method, url, (req) => { /* dynamic handler */ })
  • url can be a string, a glob pattern, or a regular expression.
  • response can be a plain object, a fixture reference, or a full response definition with statusCode/headers.
  • A function handler gives full control to inspect or modify the request, and to reply dynamically.
  • Always call .as('name') on an intercept you plan to cy.wait() on later.

cy.intercept() Cheat Sheet

Common intercept patterns you will reuse constantly.

Goal Example
Stub with a fixture cy.intercept('GET', '/api/x', { fixture: 'x.json' })
Stub with inline data cy.intercept('GET', '/api/x', { body: [] })
Stub a specific status code cy.intercept('GET', '/api/x', { statusCode: 404 })
Simulate network delay cy.intercept('GET', '/api/x', { delay: 1000, fixture: 'x.json' })
Spy without stubbing cy.intercept('POST', '/api/x').as('post')
Match with glob wildcard cy.intercept('GET', '/api/users/*')
Dynamic response handler cy.intercept('GET', '/api/x', (req) => req.reply(...))

Dynamic Handlers with req.reply()

A function passed as the intercept handler receives the request object, letting you inspect headers, query parameters, or the request body, and decide how to respond dynamically, rather than always returning the same fixed data.

cy.intercept('GET', '/api/search*', (req) => {
  const term = new URLSearchParams(req.url.split('?')[1]).get('q');
  req.reply({
    body: term === 'mouse'
      ? [{ id: 1, name: 'Wireless Mouse' }]
      : [],
  });
});

This lets a single intercept realistically respond differently based on the actual query the app sends.

Modifying a Real Response In-Flight

req.continue() lets a real request reach the actual backend, then gives you a chance to modify the real response before it reaches your application, useful for injecting a single edge-case field into otherwise-real data.

cy.intercept('GET', '/api/profile', (req) => {
  req.continue((res) => {
    res.body.subscriptionStatus = 'expired';
  });
});

Simulating Delays and Failures

Simulating slow or failing network conditions with delay and statusCode/forceNetworkError lets you test loading spinners, timeouts, and error-handling logic reliably, on demand, without needing to reproduce those conditions on a real network.

cy.intercept('GET', '/api/report', {
  delay: 3000,
  fixture: 'report.json',
}).as('getReport');

cy.get('[data-cy="loading-spinner"]').should('be.visible');
cy.wait('@getReport');
cy.get('[data-cy="loading-spinner"]').should('not.exist');

Common Mistakes

  • Setting up cy.intercept() after the request it needs to catch has already fired.
  • Forgetting .as('name'), making it impossible to cy.wait() on that specific intercept later.
  • Using an overly broad URL pattern that accidentally matches unrelated requests.
  • Stubbing a response shape that doesn't accurately reflect the real API, hiding integration bugs.

Key Takeaways

  • cy.intercept() can stub responses entirely or simply spy on real requests passing through.
  • Dynamic handlers with req.reply() and req.continue() allow fine-grained, conditional control.
  • Simulating delays and error status codes makes loading and error states reliably testable.
  • Always set up an intercept before the triggering action, and alias it for later waiting/assertions.

Pro Tip

Keep stubbed response shapes in fixture files synchronized with your real API by writing a small script (or a contract test) that periodically checks a real response against the fixture's structure, this catches API drift before it causes a false sense of security in your stubbed tests.