route
Use cy.route()
to manage the behavior of network requests.
⚠️ cy.server()
and cy.route()
are deprecated in Cypress 6.0.0. In a
future release, support for cy.server()
and cy.route()
will be moved to a
plugin. Consider using cy.intercept()
instead. See
our guide on
Migrating cy.route()
to cy.intercept()
⚠️ cy.route()
and cy.server()
only support intercepting XMLHttpRequests.
Requests using the Fetch API and other types of network requests like page loads
and <script>
tags will not be intercepted by cy.route()
and cy.server()
.
To support requests using the Fetch API you can use one of the solutions below:
- Use
cy.intercept()
which supports requests using the Fetch API and other types of network requests like page loads. Seecy.intercept()
. - Polyfill
window.fetch
to spy on and stub requests usingcy.route()
andcy.server()
by enablingexperimentalFetchPolyfill
. See #95 for more details and temporary workarounds.
Syntax
cy.route(url)
cy.route(url, response)
cy.route(method, url)
cy.route(method, url, response)
cy.route(callbackFn)
cy.route(options)
Usage
Correct Usage
cy.route('/users/**')
Arguments
url (String, Glob, RegExp)
Listen for a route matching the specific URL.
response (String, Object, Array)
Supply a response body
to stub in the matching route.
method (String)
Match the route to a specific method (GET
, POST
, PUT
, etc).
If no method is defined Cypress will match GET
requests by default.
callbackFn (Function)
Listen for a route matching a returned object literal from a callback function.
Functions that return a Promise
will automatically be awaited.
options (Object)
Pass in an options object to change the default behavior of cy.route()
. By
default cy.route()
inherits its options from
cy.server()
.
Option | Default | Description |
---|---|---|
delay | 0 | Delay for stubbed responses (in ms) |
force404 | false | Forcibly send a 404 status when the XHR does not match any existing cy.route() . |
headers | null | Response headers for stubbed routes |
method | GET | Method to match against requests |
onAbort | null | Callback function which fires anytime an XHR is aborted |
onRequest | null | Callback function when a request is sent |
onResponse | null | Callback function when a response is returned |
response | null | Response body when stubbing routes |
status | 200 | Response status code when stubbing routes |
url | null | String or RegExp url to match against request urls |
You can also set options for all cy.wait()
's
requestTimeout
and responseTimeout
globally in the
Cypress configuration to control how long to
wait for the request and response of a supplied route.
Yields
-
cy.route()
yieldsnull
. -
cy.route()
can be aliased, but otherwise cannot be chained further.
Examples
Without Stubbing
If you do not pass a response
to a route, Cypress will pass the request
through without stubbing it. We can still wait for the request to resolve later.
GET
request matching url
Wait on XHR cy.server()
cy.route('**/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
method
and url
Wait on XHR's matching cy.server()
cy.route('POST', '**/users').as('postUser')
cy.visit('/users')
cy.get('#first-name').type('Julius{enter}')
cy.wait('@postUser')
POST
to login
Setup route to
url
matching glob
Wait on Under the hood Cypress uses minimatch to
match glob patterns of url
.
This means you can take advantage of *
and **
glob support. This makes it
much easier to route against dynamic segments without having to build up a
complex RegExp
.
We expose Cypress.minimatch
as a function that you
can use in your console to test routes.
Match route against any UserId
cy.server()
cy.route('**/users/*/comments')
// https://localhost:7777/users/123/comments <-- matches
// https://localhost:7777/users/123/comments/465 <-- does not match
Use glob to match all segments
cy.server()
cy.route('**/posts/**')
// https://localhost:7777/posts/1 <-- matches
// https://localhost:7777/posts/foo/bar/baz <-- matches
// https://localhost:7777/posts/quuz?a=b&1=2 <-- matches
url
glob matching options
Override When we check glob
patterns with
minimatch, by default Cypress uses sets
matchBase
to true
. You can override this option in
cy.server()
options.
If you want to permanently override these options you could do so by setting
Cypress.Server.defaults()
.
cy.server({
urlMatchingOptions: { matchBase: false, dot: true }
})
cy.route(...)
With Stubbing
If you pass a response
to cy.route()
, Cypress will stub the response in the
request.
url
as a string
When passing a string
as the url
, the XHR's URL must match exactly what
you've written. You'll want to use the decoded string and not include any hash
encoding (ie. use @
instead of %40
).
cy.server()
cy.route('https://localhost:7777/surveys/customer?email=john@doe.com', [
{
id: 1,
name: 'john',
},
])
url
as a RegExp
When passing a RegExp as the url
, the XHR's url will be tested against the
regular expression and will apply if it passes.
cy.server()
cy.route(/users\/\d+/, { id: 1, name: 'Phoebe' })
// Application Code
$.get('https://localhost:7777/users/1337', (data) => {
console.log(data) // => {id: 1, name: "Phoebe"}
})
Response functions
You can also use a function as a response which enables you to add logic surrounding the response.
Functions that return a Promise
will automatically be awaited.
const commentsResponse = (routeData) => {
//routeData is a reference to the current route's information
return {
data: someOtherFunction(routeData),
}
}
cy.route('POST', '**/comments', commentsResponse)
Matching requests and routes
Any request that matches the method
and url
of a route will be responded to
based on the configuration of that route.
GET
is the default HTTP method used to match routes. If you want to stub a
route with another HTTP method such as POST
then you
must be explicit about the method.
If a request doesn't match any route then the behavior depends on the value of
the force404
option on the cy.server()
:
- if
force404
isfalse
(the default) then the request will pass through to the server. - if
force404
istrue
then the response will be a 404.
Specify the method
The below example matches all DELETE
requests to "/users" and stubs a response
with an empty JSON object.
cy.server()
cy.route('DELETE', '**/users/*', {})
Making multiple requests to the same route
You can test a route multiple times with unique response objects by using
aliases and
cy.wait(). Each time we use cy.wait()
for an alias,
Cypress waits for the next nth matching request.
cy.server()
cy.route('/beetles', []).as('getBeetles')
cy.get('#search').type('Weevil')
// wait for the first response to finish
cy.wait('@getBeetles')
// the results should be empty because we
// responded with an empty array first
cy.get('#beetle-results').should('be.empty')
// now re-define the /beetles response
cy.route('/beetles', [{ name: 'Geotrupidae' }])
cy.get('#search').type('Geotrupidae')
// now when we wait for 'getBeetles' again, Cypress will
// automatically know to wait for the 2nd response
cy.wait('@getBeetles')
// we responded with 1 beetle item so now we should
// have one result
cy.get('#beetle-results').should('have.length', 1)
Fixtures
Instead of writing a response inline you can automatically connect a response
with a cy.fixture()
.
cy.server()
cy.route('**/posts/*', 'fixture:logo.png').as('getLogo')
cy.route('**/users', 'fixture:users/all.json').as('getUsers')
cy.route('**/admin', 'fx:users/admin.json').as('getAdmin')
You may want to define the cy.route()
after receiving the fixture and working
with its data.
cy.fixture('user').then((user) => {
user.firstName = 'Jane'
// work with the users array here
cy.route('GET', '**/user/123', user)
})
cy.visit('/users')
cy.get('.user').should('include', 'Jane')
You can also reference fixtures as strings directly in the response by passing
an aliased fixture with @
.
cy.fixture('user').as('fxUser')
cy.route('POST', '**/users', '@fxUser')
Options
Pass in an options object
cy.server()
cy.route({
method: 'DELETE',
url: '**/user/*',
status: 412,
response: {
rolesCount: 2,
},
delay: 500,
headers: {
'X-Token': null,
},
onRequest: (xhr) => {
// do something with the
// raw XHR object when the
// request initially goes out
},
onResponse: (xhr) => {
// do something with the
// raw XHR object when the
// response comes back
},
})
Simulate a server redirect
Below we simulate the server returning 503
with a stubbed empty JSON response
body.
cy.route({
method: 'POST',
url: '**/login',
response: {
// simulate a redirect to another page
redirect: '/error',
},
})
POST
to login
Setup route to error on
headers
Change By default, Cypress will automatically set Content-Type
and Content-Length
based on what your response body
looks like.
If you'd like to override this, explicitly pass in headers
as an object
literal.
cy.route({
url: '**/user-image.png',
response: 'fx:logo.png,binary', // binary encoding
headers: {
// set content-type headers
'content-type': 'binary/octet-stream',
},
})
Use delays for responses
You can pass in a delay
option that causes a delay (in ms) to the response
for matched requests. The example below will cause the response to be delayed by
3 secs. This can be useful for testing loading states, like loading spinners, in
the DOM before the request responds.
cy.route({
method: 'PATCH',
url: '**/activities/*',
response: {},
delay: 3000,
})
Function
Set the routing options using a callback function
cy.route(() => {
// ...do some custom logic here..
// and return an appropriate routing object here
return {
method: 'POST',
url: '**/users/*/comments',
response: this.commentsFixture,
}
})
Functions that return promises are awaited
cy.route(() => {
// a silly example of async return
return new Cypress.Promise((resolve) => {
// resolve this promise after 1 second
setTimeout(() => {
resolve({
method: 'PUT',
url: '**/posts/**',
response: '@postFixture',
})
}, 1000)
})
})
Notes
Debugging
Understanding stubbed vs regular XHRs
Cypress indicates whether an XHR sent back a stubbed response or actually went out to a server in its Command Log
XHRs that display (XHR STUB)
in the Command Log have been stubbed and their
response, status, headers, and delay have been controlled by your matching
cy.route()
.
XHRs that display (XHR)
in the Command Log have not been stubbed and were
passed directly through to a server.
Cypress also logs whether the XHR was stubbed or not to the console when you click on the command in the Command Log. It will indicate whether a request was stubbed, which url it matched or that it did not match any routes.
Even the Initiator
is included, which is a stack trace to what caused the XHR
to be sent.
cy.route()
cannot be debugged using cy.request()
cy.request()
sends requests to actual endpoints, bypassing those defined using cy.route()
The intention of cy.request()
is to be used for checking endpoints on an
actual, running server without having to start the front end application.
Matches
Matching origins and non origin URL's
When Cypress matches up an outgoing XHR request to a cy.route()
, it actually
attempts to match it against both the fully qualified URL and then additionally
without the URL's origin.
cy.route('**/users/*')
The following XHRs which were xhr.open(...)
with these URLs would:
Match:
/users/1
http://localhost:2020/users/2
https://google.com/users/3
Not Match:
/users/4/foo
http://localhost:2020/users/5/foo
No matches
Requests that don't match any routes
You can force requests that do not match a route to return a 404
status and
an empty body by passing an option to the cy.server()
like so:
cy.server({ force404: true })
You can read more about this here.
Rules
Requirements
-
cy.route()
requires being chained off ofcy
.
Assertions
-
cy.route()
cannot have any assertions chained.
Timeouts
-
cy.route()
cannot time out.
Command Log
cy.server()
cy.route(/accounts/).as('accountsGet')
cy.route(/company/, 'fixtures:company').as('companyGet')
cy.route(/teams/, 'fixtures:teams').as('teamsGet')
Whenever you start a server and add routes, Cypress will display a new
Instrument Panel called Routes. It will list the routing table in the
Instrument Panel, including the method
, matched URL pattern, stubbed
,
alias
and number of matched requests:
When XHRs are made, Cypress will log them in the Command Log and indicate whether they matched a routing alias:
The circular indicator is filled if the request went to the destination server, but unfilled if the request was stubbed with a response.
Read more about request logging in Cypress.
History
Version | Changes |
---|---|
6.0.0 | Deprecated cy.route() command |