Readers like you help support MUO. When you make a purchase using links on our site, we may earn an affiliate commission. Read More.

Modern software development makes great use of APIs. They serve as a crucial link between client-side applications and backend applications, as well as between different internal or external apps.

APIs allow smooth communication and data exchange, letting software components seamlessly interact with each other. Ensuring the reliability, functionality, and performance of these APIs is of utmost importance to deliver a seamless user experience and maintain the overall integrity of the system.

MAKEUSEOF VIDEO OF THE DAY
SCROLL TO CONTINUE WITH CONTENT

It is therefore important to thoroughly test your APIs to flag and rectify bugs during development to prevent potential system failures in production environments.

Testing Node.js APIs Using Mocha, Chai, and Chai-HTTP

Mocha is a widely-used testing framework that is compatible with various JavaScript frameworks. One of its key features is a flexible test runner that simplifies the process of managing and executing test cases effectively.

It also supports various testing styles, including synchronous and asynchronous testing, allowing for a wide range of testing scenarios.

A man sitting at a desk typing on a laptop with code on the screen.

On the other hand, Chai and Chai-HTTP are assertion libraries that you can use in conjunction with Mocha. Chai provides a wide range of expressive and readable assertion interfaces such as should, expect, and assert. While, Chai-HTTP, an extension of Chai, provides an interface specifically designed for testing HTTP requests and asserting their responses.

By using Mocha in conjunction with Chai and Chai-HTTP, you can test APIs effectively. The testing workflow involves:

  • Making HTTP requests to the specified API endpoints.
  • Defining the expected responses.
  • Validating the received data from the specified source, the HTTP status codes, and more.

You can also simulate API error test scenarios that may arise in such situations and what actions should trigger in the event they occur.

You can find this project's code in its GitHub repository.

Set Up the Express.js Project and MongoDB Database

To get started, create an Express web server, and install these packages:

 npm install cors dotenv mongoose mongodb 

Next, create a MongoDB database or configure a MongoDB cluster on the cloud. Then copy the database connection URL, create a .env file in the root directory, and paste in the database connection string:

 CONNECTION_STRING="connection string" 

To finish the setup process, you need to configure the database connection and define the data models for your user data. Refer to the code in this project’s GitHub repository to:

Define the Handler Functions for the API Routes

The controller functions will manage the addition and retrieval of user data in the database. To ensure the functionality of these handler functions, you will test if they can successfully post and fetch data from the database.

In the root directory, create a controllers/userControllers.js file and add the following code:

 const User = require('../models/user.model');

exports.registerUser = async (req, res) => {
  const { username, password } = req.body;

  try {
    await User.create({ username, password});
    res.status(201).send({ message: 'User registered successfully' });
  } catch (error) {
    console.log(error);
    res.status(500).send({ message: 'An error occurred!! ' });
  }
};

exports.getUsers = async (req, res) => {
  try {
    const users = await User.find({});
    res.json(users);
  } catch (error) {
    console.log(error);
    res.status(500).send({ message: 'An error occurred!!' });
  }
};

Define the API Routes

Create a new routes/userRoutes.js file in the root directory and add the following code.

 const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/userControllers');

router.post('/api/register', userControllers.registerUser);
router.get('/api/users', userControllers.getUsers);
module.exports = router;

Define Your Server Entry Point

Update your server.js file with the following code.

 const express = require('express');
const cors = require('cors');
const app = express();
const port = 5000;
require('dotenv').config();
const connectDB = require('./utils/db');

connectDB();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());

const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);

app.listen(port, () => {
  console.log(`Server is listening at http://localhost:${port}`);
});

module.exports = app;

Write and Execute the Test Cases With Mocha

With the user API in place, go ahead and configure the test environment. First, install these packages as dev dependencies.

 npm install mocha chai chai-http --save-dev 

Now, add the following script to your package.json file.

 "scripts": {
    "test": "mocha --timeout 10000"
},

This command will execute the test cases—adding the timeout property with an appropriate value allows you to control the maximum time allowed for individual test cases to execute.

This can be useful for preventing tests from running indefinitely or completing too quickly before the test cases have finished.

Test the API Endpoints

In the root directory, create a new folder and name it test. Inside this folder, create a new user.tests.js file and add the following code for the POST endpoint test case.

 const chai = require('chai');
const chaiHttp = require('chai-http');
const app = require('../server');

chai.use(chaiHttp);
const expect = chai.expect;

describe('User API', () => {
  describe('POST /api/register', () => {
    it('should handle user registration', (done) => {
      chai.request(app)
        .post('/api/register')
        .send({ username: 'testUser', password: 'testpassword' })
        .end((err, res) => {
          if (err) {
            expect(res).to.have.status(500);
            expect(res.body).to.have.property('message').that.is.equal('An error occurred!!');
          } else {
            expect(res).to.have.status(201);
            expect(res.body).to.have.property('message').equal('User registered successfully');
          }

          done();
        });
    });
  });
});

This code defines a test case using Chai and Chai HTTP to test the user registration functionality of the user API.

It sends a POST request to the specified endpoint and makes assertions about the expected API response, verifying if the user registration functionality was successful or if an error occurred.

Here’s a breakdown of the main components of the test case:

  • expect -This object allows you to use the Chai assertion methods to make assertions about the expected response from the API.
  • describe - It describes related test cases together, in this case, tests related to the User API. The nested describe block further groups related test cases together, in this case, the POST /api/register. This helps to organize the test cases specifically related to a particular functionality.
  • it - This function describes the expected behavior of the API endpoint.
  • end - This function sends the request and provides a callback function to handle the response. The callback function performs assertions using the expect function to check the response received from the API.
  • done - This function runs to mark the end of the test case.

Finally, add the code for the GET endpoint test case right after the POST endpoint test case.

 describe('GET /api/users', () => {
  it('should fetch all user data', (done) => {
    chai.request(app)
      .get('/api/users')
      .end((err, res) => {
        if (err) {
          expect(res).to.have.status(500);
          expect(res.body).to.have.property('message').that.is.equal('An error occurred while fetching user data');
        } else {
          expect(res).to.have.status(200);
          expect(res.body).to.be.an('array');
        }

        done();
      });
  });
});

Go ahead and run the test script on your terminal to execute the two test cases.

 npm test 

If the execution of the test cases doesn’t encounter any errors, you should see similar output indicating that the tests passed successfully.

Passed API test cases report logged out on the terminal window.

Test failures can occur due to various reasons, such as network connectivity issues during HTTP requests to the database, missing required data, logical errors, and other issues.

Mocha does a good job of identifying and highlighting such errors, providing clear and detailed test reports in the logs displayed on the terminal window. This allows you to easily identify and diagnose the specific issues that caused the test failures.

Mocha Gives You No Excuse Not to Test Your APIs

While manually testing the functionality of your APIs using tools like Postman is a valid testing approach, leveraging Mocha and other testing frameworks for automated testing takes it to the next level.

With these testing tools, you can quickly and easily automate tests to cover a wide range of scenarios and edge cases. This enables you to quickly detect bugs and resolve them before deploying your APIs, ensuring you ship high-quality software to production.