Introducing cy.intercept - Next Generation Network Stubbing in Cypress 6.0

November 24, 2020

•

By The Cypress Team

Today, we're elevating the power and scope of Cypress' network handling capabilities with the introduction of the cy.intercept command in Cypress 6.0.

One of the most powerful and beloved features of Cypress are easy network stubbing and spying APIs via cy.route and cy.server commands. These commands enable mocking of network responses with test data or fixtures, creation of testing scenarios by delaying response times or manipulating status codes, and much more.

cy.intercept is the next-generation successor to cy.route by offering much more flexibility and granular control over handling of the network layer. You will now have out-of-the-box support for intercepting  fetch calls, page loads, and resource loads in addition to the pre-existing support for XMLHttpRequests (XHR).

A Quick Dive Into cy.intercept

At first glance, the cy.route and cy.intercept APIs may seem similar. For example, to stub a network route with data from a fixture:

// With cy.route
cy.server()
cy.route("/articles", "fixture:great-articles.json")

// With cy.intercept
cy.intercept("/articles", { fixture: "great-articles.json" })

There are three differences between the use of these commands in the above snippet:

  • cy.intercept does not require the use of cy.server. You can stop using cy.server once you fully switch to cy.intercept.
  • cy.route can use a shorthand string pattern (fixture:*) for specifying fixtures as the stubbed response. With cy.intercept, the same convenience is achieved with the second or StaticResponse argument.
  • The third and invisible difference is that the default request method type with cy.route is GET. This is not the case for cy.intercept. In the above usage, the response would apply to all request method types. We would need to explicitly specify the method type we wish to intercept with cy.intercept. Here is one way to do so:
// Stub GET requests to /articles with fixture
cy.intercept("GET", "/articles", { fixture: "great-articles.json" })

Now that we have seen some surface level differences, let's dive in a bit deeper. cy.intercept offers two new powerful mechanisms: routeMatcher and routeHandler. These two are input arguments, and are used like so:

cy.intercept(routeMatcher, routeHandler?)

routeMatcher is an object that declares granular matching of network calls. Beyond matching against a specific URL or path, you can also match routes against distinct headers and even basic auth credentials. For example, you can intercept and spy on a request to a specific URL that has a specific header:

cy.intercept({
  // this RegExp matches any URL beginning with '<http://api.example.com/widgets>'
  url: /^http:\\/\\/api\\.example\\.com\\/widgets/
  headers: {
    'x-requested-with': 'exampleClient'
  }
}).as("widgets")

Check out the docs for a full list of matcher properties and usage patterns.

Once a route is matched, an associated routeHandler can flexibly define what should happen with the interception. An interception can be used to make assertions on requests/responses, statically or dynamically stub responses, modify outgoing requests, and more. Let's take a look at some examples.

A routeHandler can be defined as a function that receives a request (req) object. For example, we can use this function to make assertions:

// Assert request body
cy.intercept('POST', '/organization', (req) => {
  expect(req.body).to.include('Acme Company')
})

// Assert response body
cy.intercept('/projects', (req) => {
  req.reply((res) => {
    expect(res.body).to.include('My Project')
  })
})

A routeHandler function can also be used to dynamically modify an incoming response:

cy.intercept('/notification', (req) => {
  req.reply((res) => {
    // sends a fixture body instead of the existing 'res.body'
    res.send({ fixture: 'success.json' })

    // delays the response by 1000ms
    res.delay(1000)

    // throttles the response to 64kbps
    res.throttle(64)
  })
})

If dynamic stubbing is not desired, we can achieve the same behavior with a StaticResponse object in place of a function for the routeHandler:

cy.intercept('/notification', {
	fixture: 'success.json',
	delayMs: 1000
	throttleKbps: 64
})

The versatility to dynamically handle interceptions is quite useful. For example, this functionality can be used to easily intercept specific GraphQL calls:

cy.intercept('POST', '/graphql', (req) => {
  if (req.body.includes('mutation')) {
    req.alias = 'gqlMutation'
  }
})

// assert that a matching request has been made
cy.wait('@gqlMutation')

We've only seen a quick glance at what the new cy.intercept API unlocks. Check out the docs and recipes for examples and usage patterns.

Migrating to cy.intercept

You may have already been enjoying the capabilities of cy.intercept since v5.1 via its experimental predecessor, cy.route2. After upgrading to v6.0, you can simply switch all instances of cy.route2 in your tests to cy.intercept.

If you're currently happy with cy.route, there is no rush to switch to cy.intercept immediately, as cy.route and cy.server will be deprecated in the next major version release. However, switching over to the new cy.intercept from cy.route should be straightforward along with our docs. If you are ready to make the switch here are two tips to assist your migration:

  • When stubbing responses of network routes protected by CORS and/or authentication make sure to include to appropriate headers:
cy.intercept(
  { method: "GET", url: "/bankAccounts" },
  {
    headers: {
      "access-control-allow-origin": window.location.origin,
      "Access-Control-Allow-Credentials": "true",
    },
    body: { results: [] },
  }
);
  • A 304 Not Modified status should be considered when making status code assertions on responses cached routes:
cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])

Upgrade to Cypress 6.0

In addition to cy.intercept check out 6.0 changelog for a full list of fixes and breaking changes. Also make sure to check out the docs and the migration guide to assist with your upgrade.

The Cypress team has been working hard to deliver this versatile and truly useful network handling experience. We're excited to bring these new APIs to Cypress users, and as always we're eager to hear your feedback.

Happy Testing.