If you have used Cypress in the past you should be familiar with the cy.server()
and cy.route()
methods. They enabled engineers to handle XHR requests within the browser for patterns such as mocking responses, aborting requests, and smart waiting.
Deprecated in Cypress 6.0.0, both cy.route()
and cy.server()
have been replaced by a new method, cy.intercept()
.¹
Released in November of 2020, the cy.intercept()
method allows engineers to monitor all network traffic, not just XHR requests.² Simply put, this is a game changer. It places Cypress on the same level as Puppeteer and Playwright in terms of its ability to manipulate requests within the browser.
This tutorial will showcase the power behind the intercept()
method by providing examples of request interceptions, mocks, and assertions. We will use the DemoQA Bookstore application as a base.
Getting Started
With any new JavaScript project, we must initialize using NPM.
npm init
We can begin to load Cypress once we have a package.json
file.
npm install --save-dev cypress
Once Cypress has been installed, run it with NPM so it may finish the installation.
npx cypress open
Cypress is finished installing when a cypress
directory has been created. You can additionally verify the proper installation by running npx cypress --version
.
Interception
Software applications make hundreds of network requests from the front end to the back end during user journeys. A good portion of these requests could be useful for performing actions, such as setting up test state or waiting for a response to finish before checking for an element. Request interception allows engineers to accomplish these tasks.
Intercepting a network request using Cypress sets up what is known as a spy. It allows the engineer to monitor network traffic based on a supplied URL (or URL match). More than just monitoring, spies can be expanded upon to stub responses or even make assertions against request or response body objects.
The DemoQA Bookstore application makes an API call to a books endpoint in order to fetch all books within the storefront. We can intercept this API call in order to set up a spy.
Books fetch request highlighted in red. (Screenshot by Jonathan Thompson)
Selecting this request will provide us with information that we can use to begin crafting a spy such as the request URL. In this case, the request URL is [https://www.demoqa.com/Bookstore/v1/Books](https://www.demoqa.com/Bookstore/v1/Books.)
.
We should set an alias for our request for better visibility within the test runner. Aliased requests are noted by a canary yellow label containing the name of the alias.
The beginning of an intercept to the /Books endpoint.
With our code in place, we can now run our test to verify the spy correctly monitored network traffic to the books endpoint.
An aliased request spy. (Screenshot by Jonathan Thompson)
Now that we have a spy, we can begin manipulating the request for test set up and assertions.
Stubbing Responses
A stub is a canned answer to a request. Stubs are useful in building test state within an end-to-end test by simulating data which may or may not directly affect the test. In this instance, the books endpoint returns a JSON object of data. Each individual book within the JSON object is listed within the books table.
What if this request takes a long time to load? Or, what if the table displays an error statement until the request has finished?
When writing end-to-end tests, it is critical to curate your testing environment in order to reduce flake. I have experienced the above at Pendo.io, specifically a table which returns an error statement until a request has fulfilled. We determined that we had two options to solve the problem:
- Wait for the request to finish
- Stub the data
We chose to stub the data as the table data does not directly impact our test. We can do the same with the books endpoint as the table data does not matter to the overall user journey. What matters is that a user is able to add a book to their profile. As long as there is at least one table entry, our user journey will be able to proceed.
If a stub is a canned response to a request, then our test requires a response object to be built ahead of running our test. We can do so by copying a good response object and trimming it down to a more manageable state. In this instance, we are taking the response to the books endpoint and removing all but one entry.
A book fixture.
We can stub the table data by providing a fixture to the intercepted request. This will replace the response body with whatever data is found within the fixture file.
Our test featuring a stubbed response for the books endpoint.
Our table will now be populated by a single entry titled Designing Evolvable Web APIs with ASP.NET. We can assert against this prior to taking our next action.
A properly stubbed books table. (Screenshot by Jonathan Thompson)
Assert This
Now that we have properly stubbed our data, we can harden our test by asserting against the fetch request. Doing so will allow us to ensure that the back end is communicating with the front end in the manner we expect.
The above code is now capturing the request ($req
), then capturing the response ($res
), and sending it a stub containing fixture data. The previous pattern where the intercepted URL is separated from the fixture by a comma is a shorthand version of this pattern. We have written it out longhand in order to assert against the response.
In addition to sending a fixture, we have two assertions:
- Assert status is 200
- Assert message is “OK”
This allows us to harden our test and make sure that, despite stubbed response data, the back end is still communicating with the front end. We can capture API failures using these assertions should they occur as the status and message have not been mocked.
Our response assertion. (Screenshot by Jonathan Thompson)
Completing Our Test
Our data has been stubbed, our response has been asserted. It is time that we complete our test.
Once a book is visible within the listing, the user must select the book which redirects to the individual book page. The book page contains data such as title, subtitle, and ISBN. It also contains a button for adding the book to the user's book collection.
Once a user adds a book to their collection, the book title will appear within the user's profile page.
To accurately test this user journey, our test should select the stubbed book, then assert that we are visiting the correct book page by checking against a supplied ISBN and title. We can then click on the “Add To Your Collection” button and visit the user's profile. Finally, we will assert that the book title we selected is visible within the user's book collection.
Our completed test script (authenticate and deleteBook commands not shown).
Summary
Cypress' intercept()
method is revolutionizing how we spy, stub, and test requests within web applications. Engineers can spy requests in order to monitor network traffic within a web application. Stubbing allows for simulation of test data by providing a sterile environment for testing. Asserting against responses can harden tests by providing peace of mind for front-to-back end communication.
Finally, Engineers can perform each individual action with ease due to the simplicity of Cypress' API.
Resources
- “Changelog.” Cypress Documentation, 5 Mar. 2021, docs.cypress.io/guides/references/changelog.html#6–0–0.
- Ibid.
- “Route.” Cypress Documentation, 5 Mar. 2021, docs.cypress.io/api/commands/route.html#Syntax.
- “Intercept.” Cypress Documentation, 5 Mar. 2021, docs.cypress.io/api/commands/intercept.html#Comparison-to-cy-route.
Jonathan Thompson is a Senior Quality Engineer at Pendo.io specializing in test automation. He currently resides in Raleigh, NC with his wife and a Goldendoodle named Winston. You can connect with him on LinkedIn, or follow him on either Twitter or Github.