Unit testing Node.js applications using Mocha, Chai, and Sinon
Tags: node, javascript, testing
Published: 2019-11-06 17:52:35

---
title: Unit testing Node.js applications using Mocha, Chai, and Sinon
published: true
date: 2019-11-06 17:00:57 UTC
tags: node,javascript,testing
canonical_url: https://blog.logrocket.com/unit-testing-node-js-applications-using-mocha-chai-and-sinon/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/02au9dw6ydmjldesu7tn.png
---
**Written by [Godwin Ekuma](https://blog.logrocket.com/author/godwinekuma/)**✏️

Tests help document the core features of an application. Properly written tests ensure that new features do not introduce changes that break the application.

An engineer maintaining a codebase might not necessarily be the same engineer that wrote the initial code. If the code is properly tested another engineer can confidently add new code or modify existing code with the expectation that the new changes do not break other features or, at the very least, do not cause side effects to other features.

JavaScript and Node.js have so many testing and assertion libraries like [Jest](https://jestjs.io/), [Jasmine](https://jasmine.github.io/), [Qunit](https://qunitjs.com/), and [Mocha](https://mochajs.org/). However, in this article, we will look at how to use [Mocha](https://mochajs.org/) for testing, [Chai](https://www.chaijs.com/) for assertions and [Sinon](https://sinonjs.org/) for mocks, spies, and stubs.

## Mocha

[Mocha](https://mochajs.org/) is a feature-rich JavaScript test framework running on Node.js and in the browser. It encapsulates tests in test suites (describe-block) and test cases (it-block).

Mocha has lots of interesting features:

- browser support
- simple async support, including promises
- test coverage reporting
- async test timeout support
- `before`, `after`, `beforeEach`, `afterEach` Hooks, etc.

## Chai

To make equality checks or compare expected results against actual results we can use Node.js built-in assertion module. However, when an error occurs the test cases will still pass. So Mocha recommends using other assertion libraries and for this tutorial, we will be using [Chai](https://www.chaijs.com/).

Chai exposes three assertion interfaces: expect(), assert() and should(). Any of them can be used for assertions.

## Sinon

Often, the method that is being tested is required to interact with or call other external methods. Therefore you need a utility to spy, stub, or mock those external methods. This is exactly what [Sinon](https://sinonjs.org/) does for you.

Stubs, mocks, and spies make tests more robust and less prone to breakage should dependent codes evolve or have their internals modified.

### Spy

A [spy](https://sinonjs.org/releases/v7.5.0/spies/) is a fake function that keeps track of arguments, returns value, the value of `this` and exception is thrown (if any) for all its [calls](https://sinonjs.org/releases/v7.5.0/spy-call).

### Stub

A [stub](https://sinonjs.org/releases/v7.5.0/stubs/) is a spy with predetermined behavior.

We can use a stub to:

- Take a predetermined action, like throwing an exception
- Provide a predetermined response
- Prevent a specific method from being called directly (especially when it triggers undesired behaviors like HTTP requests)

### Mock

A [mock](https://sinonjs.org/releases/v7.5.0/mocks/) is a fake function (like a spy) with pre-programmed behavior (like a stub) as well as pre-programmed expectations.

We can use a mock to:

- Verify the contract between the code under test and the external methods that it calls
- Verify that an external method is called the correct number of times
- Verify an external method is called with the correct parameters

The rule of thumb for a mock is: if you are not going to add an assertion for some specific call, don’t mock it. Use a stub instead.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Writing tests

To demonstrate what we have explained above we will be building a simple node application that creates and retrieves a user. The complete code sample for this article can be found on [CodeSandbox](https://codesandbox.io/s/github/GodwinEkuma/mocha-chai-unit-test).

### Project setup

Let’s create a new project directory for our user app project:

```jsx
mkdir mocha-unit-test && cd mocha-unit-test
mkdir src
```

Create a `package.json` file within the source folder and add the code below:

```jsx
// src/package.json
{
  "name": "mocha-unit-test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "mocha './src/**/*.test.js'",
    "start": "node src/app.js"
  },
  "keywords": [
    "mocha",
    "chai"
  ],
  "author": "Godwin Ekuma",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.18.3",
    "dotenv": "^6.2.0",
    "express": "^4.16.4",
    "jsonwebtoken": "^8.4.0",
    "morgan": "^1.9.1",
    "pg": "^7.12.1",
    "pg-hstore": "^2.3.3",
    "sequelize": "^5.19.6"
  },
  "devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^6.2.1",
    "sinon": "^7.5.0",
    "faker": "^4.1.0"
  }
}
```

Run `npm install` to install project dependencies.

Notice that test-related packages `mocha`, `chai`, `sinon` , and `faker` are saved in the dev-dependencies.

The `test` script uses a custom [glob](https://en.wikipedia.org/wiki/Glob_(programming)) (`./src/**/*.test.js`) to configure the file path of test files. Mocha will look for test files(files ending with `.test.js` ) within the directories and subdirectories of the `src` folder.

### Repositories, services, and controllers

We will structure our application using the [controller, service, and](https://stackoverflow.com/questions/40911731/what-are-repositories-services-and-actions-controllers), [repository](https://stackoverflow.com/questions/40911731/what-are-repositories-services-and-actions-controllers) pattern so our app will be broken into the repositories, services, and controllers. The Repository-Service-Controller pattern breaks up the business layer of the app into three distinct layers:

- The repository class handles getting data into and out of our data store. A repository is used between the service layer and the model layer. For example, in the `UserRepository` you would create methods that write/read a user to and from the database
- The service class calls the repository class and can combine their data to form new, more complex business objects. It is an abstraction between the controller and the repository. For example, the `UserService` would be responsible for performing the required logic in order to create a new user
- [A controller contains very little logic and is used to make calls to services](https://stackoverflow.com/questions/40911731/what-are-repositories-services-and-actions-controllers). Rarely does the controller make direct calls to the repositories unless there’s a valid reason. The controller will perform basic checks on the data returned from the services in order to send a response back to the client

Breaking down applications this way makes testing easy.

### UserRepository class

Let’s begin by creating a repository class:

```jsx
// src/user/user.repository.js
const { UserModel } = require("../database");
class UserRepository {
  constructor() {
    this.user = UserModel;
    this.user.sync({ force: true });
  }
  async create(name, email) {
    return this.user.create({
      name,
      email
    });
  }
  async getUser(id) {
    return this.user.findOne({ id });
  }
}
module.exports = UserRepository;
```

The `UserRepository` class has two methods, `create` and `getUser`. The `create` method adds a new user to the database while `getUser` method searches a user from the database.

Let’s test the `userRepository` methods below:

```jsx
// src/user/user.repository.test.js
const chai = require("chai");
const sinon = require("sinon");
const expect = chai.expect;
const faker = require("faker");
const { UserModel } = require("../database");
const UserRepository = require("./user.repository");
describe("UserRepository", function() {
  const stubValue = {
    id: faker.random.uuid(),
    name: faker.name.findName(),
    email: faker.internet.email(),
    createdAt: faker.date.past(),
    updatedAt: faker.date.past()
  };
  describe("create", function() {
    it("should add a new user to the db", async function() {
      const stub = sinon.stub(UserModel, "create").returns(stubValue);
      const userRepository = new UserRepository();
      const user = await userRepository.create(stubValue.name, stubValue.email);
      expect(stub.calledOnce).to.be.true;
      expect(user.id).to.equal(stubValue.id);
      expect(user.name).to.equal(stubValue.name);
      expect(user.email).to.equal(stubValue.email);
      expect(user.createdAt).to.equal(stubValue.createdAt);
      expect(user.updatedAt).to.equal(stubValue.updatedAt);
    });
  });
});
```

The above code is testing the `create` method of the `UserRepository` . Notice that we are stubbing the `UserModel.create` method. The stub is necessary because our goal is to test the repository and not the model. We use [`faker`](https://github.com/marak/Faker.js/) for the test fixtures:

```jsx
// src/user/user.repository.test.js

const chai = require("chai");
const sinon = require("sinon");
const expect = chai.expect;
const faker = require("faker");
const { UserModel } = require("../database");
const UserRepository = require("./user.repository");

describe("UserRepository", function() {
  const stubValue = {
    id: faker.random.uuid(),
    name: faker.name.findName(),
    email: faker.internet.email(),
    createdAt: faker.date.past(),
    updatedAt: faker.date.past()
  };
   describe("getUser", function() {
    it("should retrieve a user with specific id", async function() {
      const stub = sinon.stub(UserModel, "findOne").returns(stubValue);
      const userRepository = new UserRepository();
      const user = await userRepository.getUser(stubValue.id);
      expect(stub.calledOnce).to.be.true;
      expect(user.id).to.equal(stubValue.id);
      expect(user.name).to.equal(stubValue.name);
      expect(user.email).to.equal(stubValue.email);
      expect(user.createdAt).to.equal(stubValue.createdAt);
      expect(user.updatedAt).to.equal(stubValue.updatedAt);
    });
  });
});
```

To test the `getUser` method, we have to also stub `UserModel.findone`. We use `expect(stub.calledOnce).to.be.true` to assert that the stub is called at least once. The other assertions are checking the value returned by the `getUser` method.

### UserService class

```jsx
// src/user/user.service.js

const UserRepository = require("./user.repository");
class UserService {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }
  async create(name, email) {
    return this.userRepository.create(name, email);
  }
  getUser(id) {
    return this.userRepository.getUser(id);
  }
}
module.exports = UserService;
```

The `UserService` class also has two methods `create` and `getUser`. The `create` method calls the `create` repository method passing name and email of a new user as arguments. The `getUser` calls the repository `getUser` method.

Let’s test the `userService` methods below:

```jsx
// src/user/user.service.test.js

const chai = require("chai");
const sinon = require("sinon");
const UserRepository = require("./user.repository");
const expect = chai.expect;
const faker = require("faker");
const UserService = require("./user.service");
describe("UserService", function() {
  describe("create", function() {
    it("should create a new user", async function() {
      const stubValue = {
        id: faker.random.uuid(),
        name: faker.name.findName(),
        email: faker.internet.email(),
        createdAt: faker.date.past(),
        updatedAt: faker.date.past()
      };
      const userRepo = new UserRepository();
      const stub = sinon.stub(userRepo, "create").returns(stubValue);
      const userService = new UserService(userRepo);
      const user = await userService.create(stubValue.name, stubValue.email);
      expect(stub.calledOnce).to.be.true;
      expect(user.id).to.equal(stubValue.id);
      expect(user.name).to.equal(stubValue.name);
      expect(user.email).to.equal(stubValue.email);
      expect(user.createdAt).to.equal(stubValue.createdAt);
      expect(user.updatedAt).to.equal(stubValue.updatedAt);
    });
  });
});
```

The code above is testing the `UserService create` method. We have created a stub for the repository `create` method. The code below will test the `getUser` service method:

```jsx
const chai = require("chai");
const sinon = require("sinon");
const UserRepository = require("./user.repository");
const expect = chai.expect;
const faker = require("faker");
const UserService = require("./user.service");
describe("UserService", function() {
  describe("getUser", function() {
    it("should return a user that matches the provided id", async function() {
      const stubValue = {
        id: faker.random.uuid(),
        name: faker.name.findName(),
        email: faker.internet.email(),
        createdAt: faker.date.past(),
        updatedAt: faker.date.past()
      };
      const userRepo = new UserRepository();
      const stub = sinon.stub(userRepo, "getUser").returns(stubValue);
      const userService = new UserService(userRepo);
      const user = await userService.getUser(stubValue.id);
      expect(stub.calledOnce).to.be.true;
      expect(user.id).to.equal(stubValue.id);
      expect(user.name).to.equal(stubValue.name);
      expect(user.email).to.equal(stubValue.email);
      expect(user.createdAt).to.equal(stubValue.createdAt);
      expect(user.updatedAt).to.equal(stubValue.updatedAt);
    });
  });
});
```

Again we are stubbing the `UserRepository getUser` method. We also assert that the stub is called at least once and then assert that the return value of the method is correct.

### UserContoller class

```jsx
/ src/user/user.controller.js

class UserController {
  constructor(userService) {
    this.userService = userService;
  }
  async register(req, res, next) {
    const { name, email } = req.body;
    if (
      !name ||
      typeof name !== "string" ||
      (!email || typeof email !== "string")
    ) {
      return res.status(400).json({
        message: "Invalid Params"
      });
    }
    const user = await this.userService.create(name, email);
    return res.status(201).json({
      data: user
    });
  }
  async getUser(req, res) {
    const { id } = req.params;
    const user = await this.userService.getUser(id);
    return res.json({
      data: user
    });
  }
}
module.exports = UserController;
```

The `UserController` class has `register` and `getUser` methods as well. Each of these methods accepts two parameters `req` and `res` objects.

```jsx
// src/user/user.controller.test.js

describe("UserController", function() {
  describe("register", function() {
    let status json, res, userController, userService;
    beforeEach(() => {
      status = sinon.stub();
      json = sinon.spy();
      res = { json, status };
      status.returns(res);
      const userRepo = sinon.spy();
      userService = new UserService(userRepo);
    });
    it("should not register a user when name param is not provided", async function() {
      const req = { body: { email: faker.internet.email() } };
      await new UserController().register(req, res);
      expect(status.calledOnce).to.be.true;
      expect(status.args\[0\][0]).to.equal(400);
      expect(json.calledOnce).to.be.true;
      expect(json.args\[0\][0].message).to.equal("Invalid Params");
    });
    it("should not register a user when name and email params are not provided", async function() {
      const req = { body: {} };
      await new UserController().register(req, res);
      expect(status.calledOnce).to.be.true;
      expect(status.args\[0\][0]).to.equal(400);
      expect(json.calledOnce).to.be.true;
      expect(json.args\[0\][0].message).to.equal("Invalid Params");
    });
    it("should not register a user when email param is not provided", async function() {
      const req = { body: { name: faker.name.findName() } };
      await new UserController().register(req, res);
      expect(status.calledOnce).to.be.true;
      expect(status.args\[0\][0]).to.equal(400);
      expect(json.calledOnce).to.be.true;
      expect(json.args\[0\][0].message).to.equal("Invalid Params");
    });
    it("should register a user when email and name params are provided", async function() {
      const req = {
        body: { name: faker.name.findName(), email: faker.internet.email() }
      };
      const stubValue = {
        id: faker.random.uuid(),
        name: faker.name.findName(),
        email: faker.internet.email(),
        createdAt: faker.date.past(),
        updatedAt: faker.date.past()
      };
      const stub = sinon.stub(userService, "create").returns(stubValue);
      userController = new UserController(userService);
      await userController.register(req, res);
      expect(stub.calledOnce).to.be.true;
      expect(status.calledOnce).to.be.true;
      expect(status.args\[0\][0]).to.equal(201);
      expect(json.calledOnce).to.be.true;
      expect(json.args\[0\][0].data).to.equal(stubValue);
    });
  });
});
```

In the first three `it` blocks, we are testing that a user will not be created when one or both of the required parameters (email and name) are not provided. Notice that we are stubbing the `res.status` and spying on `res.json`:

```jsx
describe("UserController", function() {
  describe("getUser", function() {
    let req;
    let res;
    let userService;
    beforeEach(() => {
      req = { params: { id: faker.random.uuid() } };
      res = { json: function() {} };
      const userRepo = sinon.spy();
      userService = new UserService(userRepo);
    });
    it("should return a user that matches the id param", async function() {
      const stubValue = {
        id: req.params.id,
        name: faker.name.findName(),
        email: faker.internet.email(),
        createdAt: faker.date.past(),
        updatedAt: faker.date.past()
      };
      const mock = sinon.mock(res);
      mock
        .expects("json")
        .once()
        .withExactArgs({ data: stubValue });
      const stub = sinon.stub(userService, "getUser").returns(stubValue);
      userController = new UserController(userService);
      const user = await userController.getUser(req, res);
      expect(stub.calledOnce).to.be.true;
      mock.verify();
    });
  });
});
```

For the `getUser` test we mocked on the `json` method. Notice that we also had to use a spy in place `UserRepository` while creating a new instance of the `UserService`.

## Conclusion

Run the test using the command below:

```jsx
npm test
```

You should see the tests passing:

![unit tests passing in mocha chai](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2019/11/passingtestsinmochachai.png?resize=730%2C376&ssl=1)

We have seen how we can use a combination of [Mocha](https://mochajs.org/), [Chai](https://www.chaijs.com), and [Sinon](https://sinonjs.org) to create a robust test for a node application. Be sure to check out their respective documentations to broaden your knowledge of these tools. Got a question or comment? Please drop them in the comment section below.
* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/unit-testing-node-js-applications-using-mocha-chai-and-sinon/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Unit testing Node.js applications using Mocha, Chai, and Sinon](https://blog.logrocket.com/unit-testing-node-js-applications-using-mocha-chai-and-sinon/) appeared first on [LogRocket Blog](https://blog.logrocket.com).