How to Test JavaScript Middleware in Node.js?

Content verified by Anycode AI
July 21, 2024
Testing of middleware in Node.js is very critical to ensure that your application can handle requests and responses correctly. Middleware functions form the base of Express applications, as they process requests before they hit your route handlers and often modify responses on their way out to the client. Proper testing may reveal that otherwise, middleware can cause some hidden bugs or odd behavior. This guide will provide you with a step-by-step procedure on setting up and testing your middleware in JavaScript using Express, Mocha, Supertest, and Chai. One of the key features of the guide has to do with testing your middleware against a host of various scenarios to make sure that it is working as predicted, therefore making your Node.js applications more solid and easier to handle.

Testing JavaScript middleware in Node.js

Testing JavaScript middleware in Node.js is a crucial step to make sure that your middleware functions do what they're supposed to: handle incoming requests, process data, and pass control to the next middleware or route handler. Let's walk through how to test middleware using popular testing tools like Mocha, Chai, Sinon.

Setting Up the Environment

Before jumping into testing, we need to set up our development environment. First, ensure you have Node.js and npm installed. Then, initialize a new Node.js project and install the necessary dependencies.

mkdir middleware-testing
cd middleware-testing
npm init -y
npm install express mocha chai sinon supertest --save-dev
- `express`: A web framework for Node.js.
- `mocha`: A test framework for Node.js.
- `chai`: An assertion library for Node.js.
- `sinon`: Provides test spies, stubs, and mocks.
- `supertest`: For HTTP assertions in Node.js.

Writing the Middleware

Let's create a simple middleware function to test. This middleware will log request details and validate a custom header.

Create a file named middleware.js:

// middleware.js
function requestLogger(req, res, next) {
  console.log(`${req.method} ${req.url}`);
  next();
}

function validateHeader(req, res, next) {
  const headerValue = req.headers['custom-header'];
  if (!headerValue) {
    return res.status(400).json({ error: 'Custom-Header is required' });
  }
  next();
}

module.exports = { requestLogger, validateHeader };

Writing the Tests

Now, create a test file named middleware.test.js. We'll use Mocha, Chai, and Sinon for this.

// middleware.test.js
const chai = require('chai');
const sinon = require('sinon');
const sinonChai = require('sinon-chai');
const { requestLogger, validateHeader } = require('./middleware');
const express = require('express');
const request = require('supertest');

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

describe('Middleware Tests', () => {
  let app;

  beforeEach(() => {
    app = express();
  });

  describe('requestLogger', () => {
    it('should log the request method and URL', (done) => {
      const logSpy = sinon.spy(console, 'log');

      app.use(requestLogger);
      app.use((req, res) => res.send('OK'));

      request(app)
        .get('/test')
        .end((err, res) => {
          expect(logSpy).to.have.been.calledWith('GET /test');
          logSpy.restore();
          done();
        });
    });
  });

  describe('validateHeader', () => {
    it('should return 400 if custom-header is missing', (done) => {
      app.use(validateHeader);
      app.use((req, res) => res.send('OK'));

      request(app)
        .get('/test')
        .expect(400, {
          error: 'Custom-Header is required',
        }, done);
    });

    it('should pass to the next middleware if custom-header is present', (done) => {
      app.use(validateHeader);
      app.use((req, res) => res.send('OK'));

      request(app)
        .get('/test')
        .set('custom-header', 'valid-header')
        .expect(200, 'OK', done);
    });
  });
});

Explanation

- **Setting Up the Test Environment**: We use Mocha's `describe` and `it` to structure our tests. `beforeEach` ensures a new Express app is created for each test.
  
- **requestLogger Tests**:
  - We use Sinon to spy on `console.log` and verify it logs the correct details.
  - Adding `requestLogger` middleware to the app, then making a request using Supertest.
  - We check if `console.log` was called with the expected log message.

- **validateHeader Tests**:
  - We test the case when the custom header is missing, expecting a 400 status code and an error message.
  - We also test when the custom header is present, expecting the middleware to call the next middleware and return a 200 status code with an 'OK' response.

Running the Tests

To run the tests, you need to add a test script to your package.json:

"scripts": {
  "test": "mocha"
}

Then, run the tests with:

npm test

You'll see output indicating all tests have passed.

By following this guide, you can make sure that your middleware functions behave correctly in various situations. This will help you keep your codebase robust and reliable.

Have any questions?
Our CEO and CTO are happy to
answer them personally.
Get Beta Access
Anubis Watal
CTO at Anycode
Alex Hudym
CEO at Anycode