APIs form the foundation of most modern-day web applications, delivering data to the client-side code which is then parsed and rendered in the UI. Since most UI interactions operate on data retrieved from an API, you can think of end-to-end tests (i.e. tests that use your UI to replicate user workflows) as implicitly testing your API as well.
There are definitely cases where we’ll want to write tests against our APIs directly, but when thinking about doing API testing in addition to end-to-end testing, a key principle to consider is to avoid duplicative tests. We provide the following advice in our regression testing guide:
If you duplicate the same workflows across multiple tests, you’ll need to make the same updates in each of those tests when your application changes. Workflows that appear in multiple tests should be managed in a single location and referenced by other tests. Other workflows should be tested once and not duplicated.
Said another way, you should avoid UI tests and API tests that exercise the same API endpoints in a similar way. API tests are most effective as either standalone tests that are not duplicated in your UI tests, or when combined with a UI test to setup data or verify state after some UI interactions occur.
Using Cypress for API testing
Cypress is a JavaScript testing platform that enables front-end developers and test automation engineers to write automated tests for their web applications. While Cypress is mainly used for end-to-end and component testing, it can be used for API testing as well.
In this article we’ll be focusing a lot on the request() command, which is Cypress’s built-in function for making HTTP requests and will be used heavily in our API tests.
The .request() command
Cypress’s request()
command makes an HTTP request to a specified URL and returns a Promise containing the associated
HTTP response. The simplest way to call the request()
command is to pass a single parameter containing a valid URL:
|
|
Cypress automatically assumes that this request should use the HTTP GET
method, but any HTTP method can be passed
explicitly to request()
, including GET
, POST
, PUT
, DELETE
, and PATCH
.
Command chaining is a common pattern in Cypress, and its no surprise that it’s also supported in the request()
command. With command chaining, you can do things like make multiple sequential calls or add assertions against the
response returned from the request()
command like in the example below:
|
|
You can find the complete list of parameters supported by the request()
command in the
Cypress docs.
How to test APIs with Cypress
This section will cover how to install Cypress and use the request()
command to make API calls. We will also test
various parts of the response data. We will be writing tests against the
{JSON} Placeholder site, which exposes a of API endpoints that can be used for
testing purposes.
Let’s get started.
Run the command below to start up our new Cypress project. This command will create a package.json file for our project:
|
|
Next, run the command below to install Cypress as a dev dependency:
|
|
Add the scripts below to the package.json file:
|
|
With that done, run the command below to start up Cypress:
|
|
After the dialog appears, click on the Create new empty spec button. Cypress will generate a new file named
spec.cy.js
. This is the file we’ll be updating in order to test out the JSON Placeholder API.
In the code example below, I’ve created four tests that demonstrate the different type of assertions you can make against API responses:
|
|
As you can see, the code itself is pretty easy to read. The first test block makes a GET request to the JSON Placeholder API and after that, we check its status code and that it should be equal to a status code of 200.
The second test block checks that the content type includes application/json under its header, while the third section checks that the response data returns an array with exactly 50 items.
The fourth test makes a POST request to another URL given to us by the JSON Placeholder API and checking the response status code.
Now, I don’t know about you, but when developing new tests I always add an intentional failure to make sure that the failure path is working. Let’s modify the third test with an intentional failure and see if it fails, as shown in the code block below:
|
|
When re-running the test in Cypress, you should see a failure displayed with information on the expected condition (52 items returned) and the actual condition (50 items returned).
Conclusion
While tools like Postman and REST Assured are standalone tools
for writing API tests, using Cypress for API testing can be a convenient option when used alongside end-to-end tests. In
this article we covered some principles around when you should (and shouldn’t) write API tests, and how to write API
tests in Cypress via its request()
command.
Happy testing!