How to Perform Unit Testing in Node.js using Mocha and Chai

Updated: December 29, 2023 By: Guest Contributor Post a comment

Introduction

Unit testing is a vital part of software development that involves testing individual units or components of software to ensure that each one functions correctly. In Node.js, a popular choice for this is the combination of Mocha, a feature-rich JavaScript test framework running on Node.js, and Chai, a BDD/TDD assertion library. This tutorial aims to guide you through the process of setting up and writing unit tests for your Node.js applications using Mocha and Chai, showcasing various examples from basic to advanced scenarios.

Setting Up

To start, you’ll need Node.js installed on your machine. Once that’s done, you can initiate a new project and install Mocha and Chai as development dependencies using the following commands:

mkdir my-new-project
cd my-new-project
npm init -y
npm install mocha chai --save-dev

Add a ‘test’ script in your package.json that runs Mocha:

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

Basic Test Structure

A basic Mocha test looks like the following example:

const assert = require('chai').assert;
describe('Array', () => {
  describe('#indexOf()', () => {
    it('should return -1 when the value is not present', () => {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

This test checks that calling the ‘indexOf’ method on an array returns -1 when the value is not present. The ‘describe’ and ‘it’ functions structure your test suite and test cases.

Assertion Styles

Chai provides several assertion styles, the most common being Expect and Should style. The Expect style allows you to write assertions in a very natural language manner, which is highly readable:

const expect = require('chai').expect;
describe('Math', () => {
  it('does basic arithmetic', () => {
    expect(2 + 2).to.equal(4);
  });
});

The Should style extends any object with a ‘should’ property, which allows you to write very readable tests that read like English:

const should = require('chai').should();
(2 + 2).should.equal(4);

Remember that you must execute ‘should’ once before you can use it to manipulate ‘Object.prototype’:

chai.should();

Asynchronous Code Testing

Testing asynchronous code can be tricky, but Mocha makes it simple. For callback-based asynchronous code, provide a ‘done’ callback to your ‘it’ function and call it when your assertion is complete:

it('should call a callback', (done) => {
  fs.readFile('file.txt', 'utf8', (err, contents) => {
    if (err) done(err);
    contents.should.include('Hello');
    done();
  });
});

For Promise-based code, simply return the Promise and Mocha will wait for it to resolve or reject:

it('should work with promises', () => {
  return fetch('https://my.api/endpoint').then((response) => {
    expect(response.status).to.equal(200);
  });
});

Advanced Usage

Once you’re familiar with basic testing, you can explore more advanced features of Mocha and Chai like:

  • Chai plugins for extended functionalities.
  • Using ‘beforeEach’ and ‘afterEach’ for setup and teardown.
  • Timeout control for tests that require a longer execution time.
  • ‘only’ and ‘skip’ methods to control test execution.
  • Integration with other tools like Sinon for spies, stubs, and mocks.

Real-world Example

Consider a scenario where you have an API route to test:

const chai = require('chai');
const chaiHttp = require('chai-http');
const app = require('../app'); ///<- Your Express app

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

describe('/GET book', () => {
  it('it should GET all the books', (done) => {
    chai.request(app)
      .get('/book')
      .end((err, res) => {
        expect(res).to.have.status(200);
        expect(res.body).to.be.a('array');
        done();
      });
  });
});

In this example, we’re using Chai’s ‘chai-http’ plugin to make HTTP requests to our app and run assertions on the response.

Conclusion

This tutorial has covered the basics and some advanced topics in unit testing with Mocha and Chai for Node.js applications. By writing tests for your code, you’ll ensure that each part is correctly doing its job, and you can refactor with confidence. Unit tests are an essential aspect of a healthy development cycle, and mastering unit testing will make you a stronger and more capable Node.js developer.