Writing Unit Tests in ExpressJS: A Developers’ Guide

Updated: January 18, 2024 By: Guest Contributor Post a comment

Introduction

Unit testing is an essential aspect of modern web development. It enables developers to verify the functionality of individual parts of their application independently from each other, improving code reliability and facilitating smoother integration. When building web applications with Node.js, using a framework like ExpressJS, unit testing becomes especially vital due to the asynchronous nature of Node’s execution model.

Understanding Unit Testing in ExpressJS

In the context of an ExpressJS application, unit tests focus on the smallest parts of the application: the functions and methods. These include middleware, request handlers, and utility functions. The goal is to test these components in isolation to ensure they behave as expected. A robust testing framework and a well-thought-out test strategy are key to effective testing.

Choosing a Testing Framework

Several testing frameworks are suitable for Node.js applications, but for ExpressJS, popular choices include Mocha, Jest, and Jasmine. Mocha, paired with an assertion library like Chai, offers a flexible and well-documented foundation for writing tests. Jest, meanwhile, comes with everything you need bundled in, making setup simpler. This guide will focus on Mocha and Chai.

Setting Up the Environment

First, you’ll need to set up Mocha and Chai in your ExpressJS project:

  1. Install Mocha and Chai with npm: npm install mocha chai --save-dev
  2. Add a test script in your package.json file:
    {
      "scripts": {
        "test": "mocha"
      }
    }

Writing Your First Unit Test

Assuming you have a route that adds two numbers and returns the sum, let’s test it with the following code:

const expect = require('chai').expect;
const request = require('supertest');
const app = require('../app');

describe('GET /add', function() {
  it('should return the sum of two numbers', function(done) {
    request(app)
      .get('/add?num1=5&num2=10')
      .end(function(err, res) {
        expect(res.statusCode).to.equal(200);
        expect(res.body.result).to.equal(15);
        done();
      });
  });
});

Here we’re using the supertest library to simulate HTTP requests.

Testing Middleware

Middleware in ExpressJS are functions that have access to the req, res, and next objects. Writing unit tests for middleware follows similar principles as testing routes:

const expect = require('chai').expect;
const httpMocks = require('node-mocks-http');
const myMiddleware = require('../middleware/myMiddleware');

describe('MyMiddleware Function', function() {
  it('should do something', function() {
    const request  = httpMocks.createRequest();
    const response = httpMocks.createResponse();
    const next = sinon.spy();

    myMiddleware(request, response, next);
    expect(next.calledOnce).to.be.true;
  });
});

Mocking External Services

When your unit tests involve external services or databases, it is best practice to mock them:

const sinon = require('sinon');
const myDatabase = require('../database/myDatabase');

describe('Database Function', function() {
  before(function() {
    sinon.stub(myDatabase, 'getUser').resolves({ id: 1, name: 'John Doe' });
  });

  after(function() {
    myDatabase.getUser.restore();
  });

  it('should get a user by id', async function() {
    const user = await myDatabase.getUser(1);
    expect(user.name).to.equal('John Doe');
  });
});

Continuous Testing

To run tests continuously during development, consider using a tool like Nodemon to restart the Mocha test suite when changes in your codebase are detected:

  1. Install Nodemon: npm install nodemon --save-dev
  2. Change the test script in package.json to:
    {   "scripts": {     "test": "nodemon --exec mocha"   } }

Conclusion

Unit testing in ExpressJS may seem daunting at first, but with the right framework and by breaking down the process into manageable pieces, it becomes a powerful way to ensure that your application is well-built and resilient to change. Test early and often to make your development smoother and your deployments safer.