Testing APIs is an essential aspect of contemporary software development. As Node.js continues to dominate the backend world, ensuring that APIs work as expected is more important than ever. Supertest, in combination with Mocha and Chai, provides a powerful solution for automating API testing, ensuring your APIs are reliable, efficient, and ready for production.
In this blog, we’ll explore how Supertest can revolutionize your API testing in Node.js, using practical examples to illustrate how to get the most out of this fantastic tool.
Table of Contents
Why API Testing Matters
API testing ensures that the services behind your application interact properly and deliver the expected results. With more applications relying on microservices or third-party services, testing APIs ensures that:
- Data Integrity is maintained between components.
- Performance bottlenecks and bugs are caught early.
- Consistency in API responses is guaranteed for both users and services.
Without proper API testing, even small changes can introduce critical bugs, breaking functionality for users or other connected services. This is where tools like Supertest come in.
What is Supertest?
Supertest is a powerful Node.js library designed for testing HTTP APIs. It allows you to make requests to your endpoints and assert the correctness of their responses. Supertest works well with Node.js testing frameworks such as Mocha and Chai, providing a seamless experience for writing comprehensive API tests.
Key Features:
- Express Integration: Supertest integrates perfectly with Express, though it also works with other Node.js frameworks.
- Chaining Requests: Fluent API for chaining requests and expectations.
- No Server Setup Needed: Supertest allows you to test APIs directly without the need to manually spin up a server.
Setting Up Supertest with Mocha and Chai
Before we dive into writing tests, let’s set up the testing environment with Supertest, Mocha, and Chai.
Step 1: Install the Required Packages
You’ll need to install Supertest, Mocha, and Chai:
npm install supertest mocha chai --save-dev
- Mocha: A flexible test framework for running tests.
- Chai: An assertion library that complements Mocha by providing easy-to-read assertions.
- Supertest: The core API testing library.
Step 2: Example API Setup
Let’s assume you have an Express API that returns a list of users:
// app.js
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
 res.json([{ id: 1, name: 'Anmol Chugh' }, { id: 2, name: 'Rajat Chugh' }]);
});
module.exports = app;
Now that we have a simple API, let’s write some tests using Supertest.
Writing Tests with Supertest, Mocha, and Chai
Let’s test the /users endpoint to ensure it returns the correct data.
Step 1: Setting Up the Test
// test/app.test.js
const request = require('supertest');
const app = require('../app');
const chai = require('chai');
const expect = chai.expect;
describe('GET /users', () => {
 it('should return a list of users', (done) => {
  request(app)
   .get('/users')
   .expect('Content-Type', /json/)
   .expect(200)
   .end((err, res) => {
    if (err) return done(err);
    expect(res.body).to.be.an('array');
    expect(res.body.length).to.equal(2);
    expect(res.body[0]).to.have.property('id');
    expect(res.body[0]).to.have.property('name');
    done();
   });
 });
});
Test Breakdown:
- request(app): Supertest wraps the app instance, allowing you to simulate HTTP requests.
- .get(‘/users’): Makes a GET request to the /users endpoint.
- .expect(200): Asserts that the response status code is 200.
- .expect(‘Content-Type’, /json/): Asserts that the response has a Content-Type of application/json.
- Chai Assertions: Checks that the response is an array and contains the expected properties (id and name).
Testing POST Requests
Let’s add a POST endpoint that allows users to add a new user:
app.use(express.json());
app.post('/users', (req, res) => {
 const newUser = { id: 3, name: req.body.name };
 res.status(201).json(newUser);
});
Now, we’ll write a Supertest case to test the new POST endpoint:
describe('POST /users', () => {
 it('should add a new user', (done) => {
  const newUser = { name: 'Alice' };
  request(app)
   .post('/users')
   .send(newUser)
   .expect(201)
   .expect('Content-Type', /json/)
   .end((err, res) => {
    if (err) return done(err);
    expect(res.body).to.have.property('id');
    expect(res.body.name).to.equal('Alice');
    done();
   });
 });
});
Test Breakdown:
- .post(‘/users’): Sends a POST request to the /users endpoint.
- .send(newUser): Passes the data in the request body.
- Chai Assertions: Confirms the response body contains an id and the name is Alice.
Must Read: How to Install Node.js and NPM on Windows and Mac?
Refactoring with Async/Await
Supertest supports async/await, making tests cleaner and more readable. Let’s refactor the previous POST test:
describe('POST /users with async/await', () => {
 it('should add a new user', async () => {
  const newUser = { name: 'Alice' };
  const response = await request(app)
   .post('/users')
   .send(newUser)
   .expect(201)
   .expect('Content-Type', /json/);
  expect(response.body).to.have.property('id');
  expect(response.body.name).to.equal('Alice');
 });
});
Using async/await eliminates the need for callbacks, simplifying the test code.
Advanced Supertest: Handling Errors and Edge Cases
API tests should also handle edge cases, such as when invalid data is provided or when a resource is not found. Let’s add error handling to our /users/:id route:
app.get('/users/:id', (req, res) => {
 const user = users.find(u => u.id === parseInt(req.params.id));
 if (!user) return res.status(404).send('User not found');
 res.send(user);
});
Here’s a Supertest case to handle this error condition:
describe('GET /users/:id', () => {
 it('should return 404 for a non-existent user', (done) => {
  request(app)
   .get('/users/999')
   .expect(404)
   .end(done);
 });
});
This ensures that our API returns the appropriate error code (404 Not Found) when a user does not exist.
Integrating Supertest into Your CI Pipeline
Integrating Supertest into your CI/CD pipeline allows you to automatically run tests when code changes are pushed. You can integrate with tools like Jenkins, GitHub Actions, or GitLab CI to ensure your APIs remain reliable.
Conclusion
Supertest, alongside Mocha and Chai, simplifies API testing in Node.js, making it more robust and manageable. By embracing these tools, you can ensure your APIs perform well and are bug-free.
Key Takeaways:
- Supertest simplifies API testing without the need to manually start servers.
- Mocha and Chai provide clear and expressive assertions.
- Supertest handles edge cases and error testing easily.
- Async/await makes test code more readable.
By integrating Supertest into your workflow, you’ll enhance the reliability of your Node.js APIs, ensuring they’re ready for production.