Cypress API Testing: A Comprehensive Guide
By Siddharth Murugan, Community Contributor - September 25, 2023
Cypress is a highly preferred E2E testing framework and it provides us with a way to do API testing. For performing API testing, we don’t need any third-party dependency to perform this testing. We can just set up Cypress and perform API testing with it.
- What is an API? (Purpose of API testing)
- Setting up the Environment
- Writing your first Cypress API test
- Syntax
- Making HTTP Request with Cypress
- Assertions in Cypress API testing
What is an API? (Purpose of API testing)
API – Application Program Interface is a way in which two different software components communicate.
- For example, we interact with remote devices in BrowserStack from the BrowserStack website with the help of API. It acts as a medium for communication between two different software components.
- When a product is being developed, we won’t have a UI to validate the front-end part.
- But with API testing, we can validate the response we will get in the front end when the product is being developed.
Setting up the Environment
To get started with Cypress, follow the below steps:
- Create a folder where you would like to store and execute the scripts.
- Open the terminal and set up the node project with the command, npm init -y, which will create package.json file with default values.
- Execute the command, npx cypress install from the same folder in the terminal.
- Now the installation will be complete, and then the Cypress application will appear on the screen.
For executing Cypress API testing, we don’t need to install any additional packages as Cypress itself has the capacity of handling API requests and responses.
Also Read: What is Cypress Test Suite?
Writing your first Cypress API test
- To make an API request, Cypress automation provides an inbuilt command called cy.request().
- With this command, Cypress will make an HTTP request on behalf of the web application and get the response from the API server.
- When the API request is successful, we will get the response status as 2xx.
Syntax
cy.request(method,URL,body)
In the above command, method, and body are optional but the URL is mandatory.
Argument | Definition |
---|---|
method | This is the type of HTTP method we would like to implement. By default, Cypress makes this request as GET. |
URL | URL to which we need to send the request |
body | Body to send along with the request |
cy.request(options)
In the above command, the argument options should be in the form of object
Option | Default value | Description |
---|---|---|
log | true | Whether it should display the command in command log |
url | null | This is the URL to which we need to make request |
method | GET | Type of HTTP request which we need to request |
auth | null | Authorization header which needs to send along with the request |
body | null | Body of the request |
failOnStatusCode | true | Whether to make the script fail for response code other than 2xx and 3xx |
followRedirect | true | Whether to follow redirects automatically |
form | false | Whether to convert the body values to URL encoded content and set the URL encoded header |
encoding | utf8 | This is the type of encoding to be used when serializing the response body. Below are the supported encodings: ascii, base64, binary, hex, latin1, utf8, utf-8, ucs2, ucs-2, utf16le, utf-16le. |
gzip | true | Whether to accept the gzip encoding |
headers | null | Additional header which needs to be sent. |
qs | null | Query paraments to append to the URL of the request. |
retryOnStatusCodeFailure | false | This set the retry ability to Cypress request command. If it is set to true, Cypress will give a retry up to 4 times. |
retryOnNetworkFailure | true | Retry ability on network error. This will give a retry up to 4 times. |
timeout | responseTimeout | Time up to which it has to wait for the completion of the command. By default, it will wait up to the time which is mentioned in the config file responseTimeout |
Making HTTP Request with Cypress
Let’s make a simple GET request with cy.request() command. We will make a GET request to the following URL – https://www.bstackdemo.com/ with the below piece of code,
describe('template spec', () => { it('passes', () => { //Getting response from BrowserStack demo website cy.request('GET','https://www.bstackdemo.com/') }) })
You can view the request’s response from the console log of the Cypress runner by inspecting it.
Assertions in Cypress API testing
Now, let’s go ahead and try to assert the API response. When we hit the API server with the URL, if we get a response status of 200, we can say that the request we passed succeeded.
Let’s try to assert the response status code with the below piece of code:
describe('template spec', () => { it('passes', () => { //Getting response from browserstack demo website cy.request('GET','https://www.bstackdemo.com/').then((response) =>{ //Expecting the response status code to be 200 expect(response.status).to.eq(200) }) }) })
With this, we will be able to assert whether the response code which we got back is 200 or not.
Advanced Cypress API testing
Here is the list of various methods supported by Cypress:
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, COPY, LOCK, MKCOL, MOVE, PURGE, PROPFIND, PROPPATCH, UNLOCK, REPORT, MKACTIVITY, CHECKOUT, MERGE, M-SEARCH, NOTIFY, SUBSCRIBE, UNSUBSCRIBE, SEARCH, CONNECT.
But mostly, GET, POST, PUT, DELETE, and PATCH methods will be used. Let’s look at their purpose.
Method | Purpose |
---|---|
GET | This method is for getting the list of data / specific data, available from the API server |
POST | POST request is used to create data in the server |
PUT | PUT is for updating the existing data |
PATCH | This method is for updating the data partially |
DELETE | As the name implies, it is for deleting the data in target server |
Let’s try to execute them one by one, with a help of an external dummy API website – https://dummy.restapiexample.com/
With the below piece of code, you can execute POST, PUT, PATCH and DELETE request and understand it more clearly
describe('template spec', () => { //This block inserts the data which is mentioned in the body of the request it('POST request', () => { cy.request({method: 'POST', url: 'https://reqres.in/api/users', body: { "name": "Bingo", "job": "Team lead" }}).then((response) =>{ //Asserting the status code to be 201 for successful execution expect(response.status).to.eq(201) //Asserting the name which we have inserted into expect(response.body.name).to.eq("Bingo") //Asserting the status text to confirm whether it is created expect(response.statusText).to.eq("Created") }) }) //This block will execute PUT request it('PUT request', () => { cy.request({method: 'PUT', url: 'https://reqres.in/api/users/2', body: { "name": "Angel", "job": "zion resident" }}).then((response) =>{ //Asserting the status code to be 200 for successful execution expect(response.status).to.eq(200) //Asserting the name which we have inserted into expect(response.body.name).to.eq("Angel") }) }) //This block will execute the PATCH request and update the record it('PATCH request', () => { cy.request({method: 'PATCH', url: 'https://reqres.in/api/users/2', body: { "name": "Angel", "job": "zion resident" }}).then((response) =>{ //Asserting the status code to be 200 for successful execution expect(response.status).to.eq(200) //Asserting the name which we have inserted into expect(response.body.name).to.eq("Angel") }) }) //This block will delete the user which exist it('DELETE request', () => { cy.request({method: 'DELETE', url: 'https://reqres.in/api/users/2'}).then((response) =>{ //Asserting the status code to be 204 for successful execution expect(response.status).to.eq(204) }) }) })
Best Practices for Cypress API testing
When you have an API call which needs to be called across many test cases, then you can create your own custom command with Cypress and use it across many spec files. For example,
- First store the API URL as an environment variable to access it across files with Cypress.env() command.
- To store this URL as a variable, add the below object inside cypress.config.js file,
env: { myURL: "https://reqres.in/" }
- Create a custom command inside /support/commands.js file like below,
//We are creating a custom command named GETrequest which has URL and userDataLink as argument Cypress.Commands.add('GETrequest', (url,userDataLink) =>{ //Usual GET request command cy.request('GET',`${Cypress.env(url)+userDataLink}`) })
- You can use this custom command inside you spec file and get the API response stored in a variable.
- The complete working spec file, will look like below,
describe('template spec', () => { it('passes', () => { //Get the API request with URL and user details arguments and store response in a variable named details cy.GETrequest('myURL',"api/users/2").as('details') //With cy.get() validating the status code of response cy.get('@details').its('status').should('eq',200) }) })
Real-World examples of Cypress API testing
Let’s go further on testing API, by making an API GET request call to weather API, get their response, and do assertion with it.
For this step, you may need to sign-up for the weather API website, create your own account and get the API access key.Inside cypress.config.js file, paste your access key under an object named env as below,
const { defineConfig } = require("cypress"); module.exports = defineConfig({ e2e: { setupNodeEvents(on, config) { // implement node event listeners here }, env: { access_key: "<your_access_key>" } }, });
Then, we can import this access key inside our test script and make an API call with the help of below script,
describe('template spec', () => { it('passes', () => { cy.request({method: 'GET', url: 'http://api.weatherstack.com/current', qs: { //Access key given by the website access_key: Cypress.env('access_key'), //Name of the city for which you are viewing temperature query: "Chennai" }}).then((response) =>{ //Asserting the status code to be 200 for successful response expect(response.status).to.eq(200) //Asserting the location name expect(response.body.location.name).to.eq('Chennai') }) }) })
With this above piece of code, you will be able to get the API response, assert the response status code and also assert its location name in the response.
Testing a GraphQL API with Cypress
GraphQL is a query language for API. To test GraphQL API, we will use another external website – star-wars-swapi. By hitting the GraphQL API they provided, we will get the list of Star Wars movies.
Here is the working code:
describe('template spec', () => { it(GraphQL request, () => { //Making a POST request to the given URL cy.request({method: 'POST', url: 'https://swapi-graphql.netlify.app/.netlify/functions/index', body: { //Query parameter which needs to be passed for GraphQL query: `query Query { allFilms { films { title director releaseDate speciesConnection { species { name classification homeworld { name } } } } } }` } }).then((response) =>{ //Asserting the status code to be 200 for successful response expect(response.status).to.eq(200) cy.log(response) }) })
You will notice that, the only difference which we have between the usual REST API and GraphQL API is the query part. We need to pass the body request as a query.
Conclusion
With this, we are now at the end of Cypress API testing. With the above explanations, you will be able to understand the different methods of API testing, performing assertions for API testing with Cypress, best practices for Cypress API testing, real-world examples of API testing, and performing API testing for GraphQL API.
Browserstack provides you with a robust infrastructure to test on multiple devices with Cypress for: