intercept
Spy and stub network requests and responses.
Tip: We recommend you read the Network Requests guide first.
All intercepts are automatically cleared before every test.
Syntax​
// spying only
cy.intercept(url)
cy.intercept(method, url)
cy.intercept(routeMatcher)
See arguments url, method and routeMatcher
// spying and response stubbing
cy.intercept(url, staticResponse)
cy.intercept(method, url, staticResponse)
cy.intercept(routeMatcher, staticResponse)
cy.intercept(url, routeMatcher, staticResponse)
See staticResponse argument
// spying, dynamic stubbing, request modification, etc.
cy.intercept(url, routeHandler)
cy.intercept(method, url, routeHandler)
cy.intercept(routeMatcher, routeHandler)
cy.intercept(url, routeMatcher, routeHandler)
See routeHandler argument
// Specifying request and response types
type CustomRequest = {
kind: 'custom_request'
}
type CustomResponse = {
kind: 'custom_response'
}
cy.intercept<CustomRequest, CustomResponse>(url, (req) => {
req.body // .body of request will be of type CustomRequest
req.continue((res) => {
res.body // .body of response will be of type CustomResponse
})
})
Usage​
Correct Usage
// spying
cy.intercept('/users/**')
cy.intercept('GET', '/users*')
cy.intercept({
method: 'GET',
url: '/users*',
hostname: 'localhost',
})
// spying and response stubbing
cy.intercept('POST', '/users*', {
statusCode: 201,
body: {
name: 'Peter Pan',
},
})
// spying, dynamic stubbing, request modification, etc.
cy.intercept('/users*', { hostname: 'localhost' }, (req) => {
/* do something with request and/or response */
})
Arguments​
method (String)​
Match the route to a specific
HTTP method (GET,
POST, PUT, etc).
If no method is defined Cypress will match all requests by default.
url (String, Glob, RegExp)​
Specify the URL to match. See Matching url for examples.
Alternatively, specify the URL via the routeMatcher
argument (below).
routeMatcher (RouteMatcher)​
routeMatcher is an object used to match the incoming HTTP requests with this
intercepted route.
All properties are optional but all those that are set must match for the
request to be intercepted. If a string is passed to any property, it will be
glob-matched against the request using
Cypress.minimatch with the { matchBase: true }
minimatch option applied.
| Option | Description |
|---|---|
| auth | HTTP Basic Authentication (object with keys username and password) |
| headers | HTTP request headers (object) |
| hostname | HTTP request hostname |
| https | true: only secure (https://) requests, false: only insecure (http://) requests |
| method | HTTP request method (matches any method by default) |
| middleware | true: match route first and in defined order, false: match route in reverse order (default) |
| path | HTTP request path after the hostname, including query parameters |
| pathname | Like path, but without query parameters |
| port | HTTP request port(s) (number or Array) |
| query | Parsed query string (object) |
| resourceType deprecated | The resource type of the request. See "Request object properties" for a list of possible values for resourceType. |
| times | Maximum number of times to match (number) |
| url | Full HTTP request URL |
See examples below.
staticResponse (StaticResponse)​
By passing in a StaticResponse as the last argument, you can
statically define (stub) a response for matched
requests. See StaticResponse object for the list of
properties.
Additionally, you can pass { log: false } with your StaticResponse to
disable command logs for this intercept. See
"Disabling logs for a request".
See
Stubbing a response with a StaticResponse object
for an example.
routeHandler (Function)​
The routeHandler function is called whenever a request is matched, with the
first argument being the request object. From inside the callback, you have
access to the entire request-response where you can modify the outgoing request,
send a response, access the real response, and more.
See "Intercepted requests" and
Request/Response Modification with routeHandler.
Yields ​
cy.intercept()yieldsnull, but can be aliased.- Waiting on an aliased
cy.intercept()route using cy.wait() will yield an object that contains information about the matching request/response cycle. See Using the yielded object for examples of how to use this object.
Examples​
Matching url​
You can provide the exact URL to match or use pattern-matching to match many URLs at once, either with globs or with regex. See Glob Pattern Matching URLs.
// match any request that exactly matches the URL
cy.intercept('https://prod.cypress.io/users')
// match any request that satisfies a glob pattern
cy.intercept('/users\\?_limit=*')
// match any request that satisfies a regex pattern
cy.intercept(/\/users\?_limit=(3|5)$/)
Matching method​
If you don't pass in a method argument, then all HTTP methods
(GET, POST, PUT, PATCH, DELETE, etc.) will match.
cy.intercept('/users')
// matches this: GET http://localhost/users
// ...and this, too: POST http://localhost/users
cy.intercept('GET', '/users')
// matches this: GET http://localhost/users
// ...but not this: POST http://localhost/users
Matching with RouteMatcher​
Specifying a method and url to match can also be acheived by passing the
routeMatcher object into cy.intercept instead:
// These both yield the same result:
cy.intercept({ method: 'GET', url: '**/users' })
cy.intercept('GET', '**/users')
// Match any type of request with the pathname `/search`
// and the query parameter 'q=some+terms'
cy.intercept({
pathname: '/search',
query: {
q: 'some terms',
},
}).as('searchForTerms')
cy.intercept(
{
// this RegExp matches any URL beginning with
// 'http://api.example.com/' and ending with '/edit' or '/save'
url: /^http:\/\/api\.example\.com\/.*\/(edit|save)/,
// matching requests must also contain this header
headers: {
'x-requested-with': 'exampleClient',
},
}
})
// this example will cause 1 request to `/temporary-error`
// to receive a network error and subsequent requests will
// not match this `RouteMatcher`
cy.intercept({ url: '/temporary-error', times: 1 }, { forceNetworkError: true })
Pattern Matching​
// match updates to the `/users` endpoint using glob matching
cy.intercept({
method: '+(PUT|PATCH)',
url: '**/users/*',
})
// matches:
// PUT /users/1
// PATCH /users/1
//
// doesn't match:
// GET /users
// GET /users/1
// same as above, but using regex
cy.intercept({
method: '/PUT|PATCH/',
url: '**/users/*',
})
Aliasing an intercepted route​
While cy.intercept doesn't yield anything, you can chain
.as to it to create an
alias which can be used
to wait on a request.
cy.intercept('GET', '/users').as('getAllUsers')
cy.intercept('POST', '/users').as('createUser')
Accessing all intercepted requests​
When an intercepted route is aliased, you can retrieve all matching
intercepted requests using the .all suffix with
cy.get(). This is useful when your application makes
multiple requests to the same route and you need to assert on all of them.
cy.intercept('GET', '/users').as('getUsers')
// trigger multiple requests
cy.visit('/users')
cy.visit('/users')
// yields an array of all interceptions for this alias
cy.get('@getUsers.all').then((interceptions) => {
expect(interceptions).to.have.length(2)
})
You can also retrieve a specific intercept by its 1-based numeric index. Without
any suffix, cy.get('@alias') returns the most recent intercepted request.
cy.intercept('GET', '/users').as('getUsers')
cy.visit('/users')
cy.visit('/users')
// yields the first intercepted request
cy.get('@getUsers.1').its('response.statusCode').should('eq', 200)
// yields the second intercepted request
cy.get('@getUsers.2').its('response.statusCode').should('eq', 200)
Index 0 is not valid — indices start at 1. These suffixes are not
supported by cy.wait(). Use cy.get('@alias.all')
instead of cy.wait('@alias.all').
Aliasing individual requests​
Aliases can be set on a per-request basis by setting the alias property of the
intercepted request. This is especially useful when intercepting GraphQL
requests:
cy.intercept('POST', '/graphql', (req) => {
if (req.body.hasOwnProperty('query') && req.body.query.includes('mutation')) {
req.alias = 'gqlMutation'
}
})
// assert that a matching request has been made
cy.wait('@gqlMutation')
For more guidance around aliasing requests with GraphQL, see Working with GraphQL.
Waiting on a request​
Use cy.wait() with aliasing an intercepted route to wait for the request/response cycle to complete.
With URL​
cy.intercept('http://example.com/settings').as('getSettings')
// once a request to get settings responds, 'cy.wait' will resolve
cy.wait('@getSettings')
With RouteMatcher​
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}).as('search')
// once any type of request to search with a querystring
// containing 'q=expected+terms' responds, 'cy.wait' will resolve
cy.wait('@search')
Using the yielded object​
Using cy.wait() on a cy.intercept() route alias yields
an interception object which represents the request/response cycle:
cy.wait('@someRoute').then((interception) => {
// 'interception' is an object with properties
// 'id', 'request' and 'response'
})
You can chain .its() and
.should() to assert against request/response cycles:
// assert that a request to this route
// was made with a body that included 'user'
cy.wait('@someRoute').its('request.body').should('include', 'user')
// assert that a request to this route
// received a response with HTTP status 500
cy.wait('@someRoute').its('response.statusCode').should('eq', 500)
// assert that a request to this route
// received a response body that includes 'id'
cy.wait('@someRoute').its('response.body').should('include', 'id')
Waiting on errors​
You can use cy.wait() to wait on requests that end with network errors:
cy.intercept('GET', '/should-err', { forceNetworkError: true }).as('err')
// assert that this request happened
// and that it ended in an error
cy.wait('@err').should('have.property', 'error')
Stubbing a response​
With a string​
// requests to '/update' will be fulfilled
// with a body of "success"
cy.intercept('/update', 'success')
With a fixture​
// requests to '/users.json' will be fulfilled
// with the contents of the "users.json" fixture
cy.intercept('/users.json', { fixture: 'users.json' })
With a StaticResponse object​
A StaticResponse object represents a response to an HTTP
request, and can be used to stub routes:
const staticResponse = {
/* some StaticResponse properties here... */
}
cy.intercept('/projects', staticResponse)
Stub a response with a JSON body:
cy.intercept('/projects', {
body: [{ projectId: '1' }, { projectId: '2' }],
})
Stub headers, status code, and body all at once:
cy.intercept('/not-found', {
statusCode: 404,
body: '404 Not Found!',
headers: {
'x-not-found': 'true',
},
})
Stub response with a fixture that is read as a Buffer:
cy.intercept('/not-found', {
fixture: 'media/gif.mp4,null',
})
See also StaticResponse object.
Using the routeHandler function​
By specifying a routeHandler function as the last argument
to cy.intercept, you'll have access to the entire request-response session,
enabling you to modify the outgoing request, manipulate the real response, make
assertions, etc.
The routeHandler takes the incoming HTTP request (IncomingHTTPRequest) as
the first argument.
cy.intercept('/users*', (req) => {
/* do something with request and/or response */
})
Throughout these examples we will refer to the incoming HTTP request as req.
Those of you with Express.js
middleware experience
should be familiar with this syntax.
Asserting on a request​
cy.intercept('POST', '/organization', (req) => {
expect(req.body).to.include('Acme Company')
})
Modifying an outgoing request​
You can use the request handler callback to modify the intercepted request object before it is sent.
// set the request body to something different
// before it's sent to the destination
cy.intercept('POST', '/login', (req) => {
req.body = 'username=janelane&password=secret123'
})
// dynamically set the alias
cy.intercept('POST', '/login', (req) => {
req.alias = 'login'
})
Adding a header to an outgoing request​
You can add a header to an outgoing request, or modify an existing header
cy.intercept('/req-headers', (req) => {
req.headers['x-custom-headers'] = 'added by cy.intercept'
})
Note: the new header will NOT be shown in the browser's Network tab, as the request has already left the browser. You can still confirm the header was added by waiting on the intercept as shown below:
Waiting on the intercept​
cy.intercept('/req-headers', (req) => {
req.headers['x-custom-headers'] = 'added by cy.intercept'
}).as('headers')
// the application makes the call ...
// confirm the custom header was added
cy.wait('@headers')
.its('request.headers')
.should('have.property', 'x-custom-headers', 'added by cy.intercept')
Add, modify or delete a header to all outgoing requests​
You can add, modify or delete a header to all outgoing requests using a
beforeEach() in the
supportFile.
beforeEach(() => {
cy.intercept(
{ url: 'http://localhost:3001/**', middleware: true },
// Delete 'if-none-match' header from all outgoing requests
(req) => delete req.headers['if-none-match']
)
})
Dynamically stubbing a response​
You can use the req.reply() function to dynamically control the
response to a request.
cy.intercept('/billing', (req) => {
// functions on 'req' can be used to
// dynamically respond to a request here
// send the request to the destination server
req.reply()
// respond to the request with a JSON object
req.reply({ plan: 'starter' })
// send the request to the destination server
// and intercept the response
req.continue((res) => {
// 'res' represents the real destination's response
// See "Intercepting a response" for more details and examples
})
})
See "Intercepted requests" for more information on the req object and
its properties and methods.
Returning a Promise​
If a Promise is returned from the route callback, it will be awaited before continuing with the request.
cy.intercept('POST', '/login', (req) => {
// you could asynchronously fetch test data...
return getLoginCredentials().then((credentials) => {
// ...and then, use it to supplement the outgoing request
req.headers['authorization'] = credentials
})
})
Passing a request to the next request handler​
If req.reply() or req.continue() is not
explicitly called inside of a request handler, requests will pass to the next
request handler until none are left.
// you could have a top-level middleware handler that
// sets an auth token on all requests
// but remember setting `middleware: true` will
// cause this to always be called first
cy.intercept('http://api.company.com/', { middleware: true }, (req) => {
req.headers['authorization'] = `token ${token}`
})
// and then have another handler that
// more narrowly asserts on certain requests
cy.intercept('POST', 'http://api.company.com/widgets', (req) => {
expect(req.body).to.include('analytics')
})
// a POST request to http://api.company.com/widgets would hit both
// of those callbacks, middleware first, then the request would be
// sent out with the modified request headers to the
// real destination
Disabling logs for a request​
By default, Cypress logs all requests that match any cy.intercept(), as well
as all XMLHttpRequests and fetch requests. You can use cy.intercept() to
disable these logs by passing { log: false } in the second parameter:
// disable Cypress's default behavior of logging all XMLHttpRequests and fetches
cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })
Note: Currently, you can only enable/disable a request's logs when defining the
cy.intercept(), not inside of an intercept callback. See
#26069.
Intercepting a response​
Inside of a callback passed to req.continue(), you can access the destination
server's real response.
cy.intercept('/integrations', (req) => {
// req.continue() with a callback will send the request to
// the destination server
req.continue((res) => {
// 'res' represents the real destination response
// you can manipulate 'res' before it's sent to the browser
})
})
See "Intercepted responses" for more information on the res object. See
"Controlling the outbound request with req.continue()" for
more information about req.continue().