Introduction
Cypress is widely known for its end-to-end testing capabilities, but it’s also a powerful tool for testing APIs. API testing is an essential part of ensuring the reliability, performance, and security of modern web applications, as many applications today heavily depend on backend services and APIs. This post will walk you through the basics of API testing using Cypress, including setting up API tests, making requests, validating responses, and automating the process.
Why Use Cypress for API Testing?
- Built-in Features: Cypress has built-in capabilities for making HTTP requests, assertions, and managing asynchronous tests.
- Easy Integration: You can easily integrate API tests into your existing Cypress test suite, alongside your UI tests.
- Great Developer Experience: Cypress offers a user-friendly interface, fast feedback, and real-time debugging.
- Automation: You can automate API tests in your CI/CD pipeline, ensuring that APIs are consistently tested with every deployment.
Getting Started with API Testing in Cypress
If you already have Cypress installed for end-to-end testing, you’re ready to start API testing. If not, follow the installation steps below.
Step 1: Install Cypress
npm install cypress --save-dev
Once installed, you can run Cypress with the following command:
npx cypress open
Step 2: Setting Up Your First API Test
Cypress provides a cy.request()
method that you can use to send HTTP requests. Here’s a basic example of testing an API that returns a list of users:
describe('API Testing with Cypress', () => {
it('should retrieve a list of users', () => {
cy.request('GET', 'https://jsonplaceholder.typicode.com/users')
.then((response) => {
// Validate the status code
expect(response.status).to.eq(200);
// Validate the response body
expect(response.body).to.have.length(10);
expect(response.body[0]).to.have.property('name');
});
});
});
In this example, we’re testing a GET request to retrieve a list of users. Cypress validates the response status code and checks that the response body contains 10 users.
Common HTTP Methods in API Testing
Here’s how you can perform common HTTP requests like GET, POST, PUT, and DELETE in Cypress.
GET Request
GET requests are used to retrieve data from the server. For example:
cy.request('GET', 'https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.property('id', 1);
});
POST Request
POST requests are used to create new resources on the server. Here’s how to test a POST request:
cy.request('POST', 'https://jsonplaceholder.typicode.com/posts', {
title: 'New Post',
body: 'This is the content of the new post.',
userId: 1
})
.then((response) => {
expect(response.status).to.eq(201);
expect(response.body).to.have.property('title', 'New Post');
});
PUT Request
PUT requests are used to update existing resources. Example:
cy.request('PUT', 'https://jsonplaceholder.typicode.com/posts/1', {
id: 1,
title: 'Updated Post Title',
body: 'Updated content',
userId: 1
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.body).to.have.property('title', 'Updated Post Title');
});
DELETE Request
DELETE requests are used to remove resources. Example:
cy.request('DELETE', 'https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
expect(response.status).to.eq(200);
});
Advanced API Testing Techniques
Once you have mastered basic API requests, you can move on to more advanced API testing techniques, such as handling authentication, testing API failures, and chaining API calls.
Handling Authentication
Some APIs require authentication via tokens or API keys. Cypress can handle authentication by setting custom headers. For example:
cy.request({
method: 'GET',
url: 'https://api.example.com/protected',
headers: {
Authorization: `Bearer ${Cypress.env('auth_token')}`
}
})
.then((response) => {
expect(response.status).to.eq(200);
});
Here, the Authorization
header is used to send a token along with the request.
Testing API Failures
It’s essential to test how your API behaves when something goes wrong, such as invalid inputs or missing resources. For example, testing a 404 error:
cy.request({
method: 'GET',
url: 'https://jsonplaceholder.typicode.com/posts/9999',
failOnStatusCode: false
})
.then((response) => {
expect(response.status).to.eq(404);
});
By setting failOnStatusCode: false
, Cypress won’t fail the test automatically for non-2xx responses, allowing you to validate error responses.
Chaining API Calls
Sometimes, you need to chain multiple API calls together, for example, creating a resource and then using it in a subsequent test:
cy.request('POST', 'https://jsonplaceholder.typicode.com/posts', {
title: 'Post for chaining',
body: 'Chained post content',
userId: 1
}).then((postResponse) => {
cy.request('GET', `https://jsonplaceholder.typicode.com/posts/${postResponse.body.id}`)
.then((getResponse) => {
expect(getResponse.body).to.have.property('title', 'Post for chaining');
});
});
In this example, the POST
request creates a new post, and the GET
request retrieves that post based on the ID returned from the first call.
Automating API Tests in CI/CD Pipelines
To ensure the stability of your APIs, it’s crucial to run your API tests as part of your Continuous Integration/Continuous Deployment (CI/CD) pipeline. Cypress can be easily integrated into CI/CD tools like Jenkins, GitHub Actions, or Travis CI.
Here’s an example of a GitHub Actions workflow that runs Cypress tests on every push:
name: CI
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm install
- name: Run Cypress tests
run: npx cypress run
This workflow ensures that API tests are automatically executed every time code is pushed to the main branch.
Best Practices for API Testing with Cypress
To ensure that your API tests are reliable, maintainable, and efficient, follow these best practices:
- Use Environment Variables: Store sensitive data like API keys, tokens, or base URLs in environment variables.
-
Mock Responses Where Necessary: If you don’t want to hit the real API every time, mock the API responses using
cy.intercept()
. - Handle Rate Limits: If your API has rate limits, make sure your tests handle them appropriately by adding retries or delays between requests.
- Keep Tests Independent: Ensure that each API test is independent of the others, so they can be run in any order without dependency issues.
- Focus on Edge Cases: Test both the happy path and edge cases (e.g., invalid inputs, large data sets, rate limiting) to cover all potential scenarios.
Conclusion
API testing is an essential part of ensuring that your application works seamlessly, even when it relies on external services or backend APIs. Cypress provides a simple yet powerful way to test APIs alongside your UI tests, making it an excellent choice for developers working with modern JavaScript applications. By leveraging features like cy.request()
, assertions, and CI/CD integration, you can build a robust and reliable API test suite that catches issues early and ensures the quality of your APIs.
Start testing your APIs with Cypress today, and improve your development workflow with automated, reliable tests!