Network Requests with Cypress
TestJS Summit 2021
Cecelia Martinez, Cypress
@ceceliacreates
Network Requests + Cypress
Cypress executes tests against your application inside the browser
@ceceliacreates
Network Requests + Cypress
@ceceliacreates
View network requests as they occur in the Command Log while testing
Network Requests + Cypress
@ceceliacreates
Click any request in the Command Log to output to the console
Network Requests + Cypress
@ceceliacreates
cy.request()
Execute HTTP requests
cy.intercept()
Interact with network requests
Cypress Real World App
@ceceliacreates
cy.request()
How it works
- Send an HTTP request from your test code
- Separate from the functionality of your app
@ceceliacreates
cy.request(url)
cy.request(url, body)
cy.request(method, url)
cy.request(method, url, body)
cy.request(options)
Use cases
- API Testing
- Retrieve, Create, or Update data for testing
- Validate endpoint before proceeding with test
@ceceliacreates
context("GET /bankAccounts", function () {
it("gets a list of bank accounts for user", function () {
const { id: userId } = ctx.authenticatedUser!;
cy.request("GET", `${apiBankAccounts}`).then((response) => {
expect(response.status).to.eq(200);
expect(response.body.results[0].userId).to.eq(userId);
});
});
});
cy.intercept()
How it works
- Cypress routes all HTTP requests - including XMLHttpRequest (XHR) and fetch - through its proxy
- Any request can be observed or stubbed
- route handler can be programmatic
@ceceliacreates
Network Spying
Declaring a spy with cy.intercept()
- Can pass a URL, method, or routeMatcher
- If no method is passed, ANY method types will match
cy.intercept(url)
cy.intercept(method, url)
cy.intercept(routeMatcher)
// with routeMatcher
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}).as('search')
- routeMatcher is an object used to match which HTTP requests will be handled
-
All properties are optional
-
auth, headers, hostname, https, method, middleware, path, pathname, port, query, times, url
-
Aliasing a spy with .as()
Save the intercepted request to use throughout your test code
cy.intercept({
url: 'http://example.com/search*',
query: { q: 'expected terms' },
}).as('search')
Waiting for a request with .wait()
After declaring an intercept, use its alias to wait for the request to occur in your test code
cy.intercept('GET', '/users').as('getUsers')
// test code
cy.wait('@getUsers')
// test code continues after request occurs
Dynamic matching
- Useful for aliasing GraphQL requests
- (req) gives us access to cy.intercept API commands
- Leverage req.alias instead of .as() to only alias if matched
cy.intercept("POST", apiGraphQL, (req) => {
const { body } = req;
if (body.hasOwnProperty("query") && body.query.includes("listBankAccount")) {
req.alias = "gqlListBankAccountQuery";
}
});
Network Requests
Asserting on network requests
cy.intercept('GET', '/users').as('getUsers')
// We can make assertions on the request and response
cy.wait('@getUsers').its('request.url').should('include', 'users')
cy.wait('@getUsers').then((interception) => {
// we can now access the low level interception
// that contains the request body, response body, status, etc
expect(interception.request.url).to.include('users')
})
Network Requests
Waiting for multiple requests
cy.intercept('GET', '/users').as('getUsers')
// We can make assertions on the request and response
cy.wait('@getUsers').its('request.body').should('include', 'user1')
// Create new user and wait for the request again
cy.wait('@getUsers').its('request.body').should('include', 'user2')
Network Stubbing
Network Stubbing
// stubs response with a fixture
cy.intercept('/users.json', { fixture: 'users.json' })
// stubs response with JSON body
cy.intercept('/projects', {
body: [{ projectId: '1' }, { projectId: '2' }],
})
// stubs status, headers, and body
cy.intercept('/not-found', {
statusCode: 404,
body: '404 Not Found!',
headers: {
'x-not-found': 'true',
},
})
Pass a response body to stub the actual network response
Dynamic Stubbing
cy.intercept('/billing', (req) => {
// dynamically get billing plan name at request-time
const planName = getPlanName()
// this object will automatically be JSON.stringified and sent as the response
req.reply({ plan: planName })
})
- The req.reply() function can be used to send a stub response for an intercepted request
- Passing a string, object, or StaticResponse
- With a StaticResponse, you can force a network error, delay/throttle the response, send a fixture, and more
Handling multiple requests
cy.intercept('login', {'user': 'username1', 'authenticated': 'true'}).as('login')
// test code to trigger the network request and stubbed response
cy.wait('@login').its('request.body').should('include', 'username1')
// response returns username1
cy.intercept('login', {'user': 'username2', 'authenticated': 'true'}).as('login')
// test code to trigger the network request and stubbed response
cy.wait('@login').its('request.body').should('include', 'username2')
// response now returns username2
- As of version 7.0, request handlers supplied to cy.intercept() are now matched starting with the most recently defined request interceptor
- This allows users to override request handlers by calling cy.intercept() again
Pros and Cons of Network Stubbing
Approach: Real Server Responses
- By not stubbing your responses, you are writing true end-to-end tests
- Guarantees the contract between your client and server is working correctly
- Suggested use: use sparingly, great for the critical paths of your application
Pros
- ✅ More likely to work in production
- ✅ Test coverage around server endpoints
- ✅ Great for traditional server-side HTML rendering
Cons
- ❌ Requires seeding data
- ❌ Much slower
- ❌ Harder to test edge cases
Approach: Stub Server Responses
- Enables you to control every aspect of the response
- A great way to control the data that is returned to your client
- Suggested use: Mix and match, typically have one true end-to-end test, and then stub the rest
Pros
- ✅ Control of response bodies, status, and headers
- ✅ Can force responses to take longer to simulate network delay
- ✅ No code changes to your server or client code
- ✅ Fast, < 20ms response times
Cons
- ❌ No guarantee your stubbed responses match the actual data
- ❌ No test coverage on some server endpoints
- ❌ Not as useful if for traditional server side HTML rendering
Network Requests with Cypress
TestJS Summit 2021
Cecelia Martinez, Cypress
@ceceliacreates
Network Requests with Cypress
By Cecelia Martinez
Network Requests with Cypress
Whether you’re testing your UI or API, Cypress gives you all the tools needed to work with and manage network requests. This intermediate-level talk demonstrates how to use the cy.request and cy.intercept commands to execute, spy on, and stub network requests while testing your application in the browser. Learn how the commands work as well as use cases for each, including best practices for testing and mocking your network requests.
- 2,320