Unit and integration testing for Node.js apps
Tags: node, javascript, testing
Published: 2019-11-12 21:06:36

---
title: Unit and integration testing for Node.js apps
published: true
date: 2019-11-12 18:00:04 UTC
tags: node,javascript,testing
canonical_url: https://blog.logrocket.com/unit-and-integration-testing-for-node-js-apps/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/8bi8iepbr3odfk47gx5n.png
---
**Written by [Andrew Evans](https://blog.logrocket.com/author/andrewevans/)**✏️

With any application, testing is an integral part of the development process.

Building tests with your application enables you to:

- Quickly verify that changes to a project do not break expected behavior
- Act as pseudo documentation as path flows are documented
- Easily demonstrate application behaviors
- Quickly take a review of your application’s health and codebase

This post is going to introduce unit and integration testing of Node.js applications.

We’re going to be reviewing my Express.js API ms-starwars, which is [on GitHub here](https://github.com/andrewevans0102/ms-starwars). I recommend doing a `git clone` of my project and following along as I discuss different ways to unit test the application.

[![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/)

### An overview of testing

When testing with Node.js, you’ll typically use the following:

- [mocha](https://mochajs.org/)
- [chai](https://www.chaijs.com/)
- [chai-http](https://www.npmjs.com/package/chai-http)
- [sinon](https://sinonjs.org/)

The term testing also typically refers to the following:

- **unit testing** – testing your application code and logic. This is anything that your code actually does and is not reliant upon external services and data to accomplish.
- **integration testing** – testing your application as it connects with services inside (or outside) of your application. This could include connecting different parts of your application, or connecting two different applications in a larger umbrella project.
- regression testing – testing your application behaviors after a set of changes have been made. This is typically something you do before major product releases.
- **end-to-end testing** – testing the full end-to-end flow of your project. This includes external HTTP calls and complete flows within your project.

Beyond these four, there are also other forms of testing specific to applications and frameworks.

In this post, we’re going to focus on unit and integration testing.

First, let’s discuss the different frameworks we’ll be using.

### What is mocha?

Mocha is a test runner that enables you to exercise your Node.js code. It works well with any Node.js project, and follows the basic [Jasmine](https://jasmine.github.io/2.0/introduction.html) syntax similar to the following (borrowed from [the mocha getting started docs](https://mochajs.org/#getting-started).

```jsx
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});
```

With mocha, you can also include the use of assertion libraries like [assert](https://www.npmjs.com/package/assert), [expect](https://www.npmjs.com/package/expect), and others.

Mocha also has many features within the test runner itself. I highly recommend reading [A quick and complete guide to Mocha testing](https://blog.logrocket.com/a-quick-and-complete-guide-to-mocha-testing-d0e0ea09f09d/) by Glad Chinda for more information.

### What is chai and chai-http?

[Chai](https://www.chaijs.com/) offers an assertion library for Node.js.

Chai includes basic assertions that you can use to verify behavior. Some of the more popular ones include:

- should
- expect
- assert

These can be used in your tests to evaluate conditions of code you’re testing, such as the following [borrowed from chai’s homepage](https://www.chaijs.com/):

```jsx
chai.should();

foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.lengthOf(3);
tea.should.have.property('flavors')
  .with.lengthOf(3);
```

[Chai-http](https://www.chaijs.com/plugins/chai-http/) is a plugin that offers a full-fledged test runner that will actually run your application and test its endpoints directly:

```jsx
describe('GET /films-list', () => {
  it('should return a list of films when called', done => {
    chai
      .request(app)
      .get('/films-list')
      .end((err, res) => {
        res.should.have.status(200);
        expect(res.body).to.deep.equal(starwarsFilmListMock);
        done();
      });
  });
});
```

With chai-http, the test runner starts your application, calls the requested endpoint, and then brings it down all in one command.

This is really powerful and helps with integration testing of your application.

### What is sinon?

In addition to having a test runner and assertions, testing also requires spying, stubbing, and mocking. [Sinon](https://sinonjs.org/) provides a framework for spys, stubs, and mocks with your Node.js tests.

Sinon is fairly straightforward, and you just use the associated spy, stub, and mock objects for different tests in your application.

A simple test with some stubs from sinon would look like this:

```jsx
describe('Station Information', function() {
  afterEach(function() {
    wmata.stationInformation.restore();
  });
  it('should return station information when called', async function() {
    const lineCode = 'SV';
    const stationListStub = sinon
      .stub(wmata, 'stationInformation')
      .withArgs(lineCode)
      .returns(wmataStationInformationMock);
    const response = await metro.getStationInformation(lineCode);
    expect(response).to.deep.equal(metroStationInformationMock);
  });
});
```

I know there’s a lot going on here, but lets just pay attention to this:

```jsx
const stationListStub = sinon
      .stub(wmata, 'stationInformation')
      .withArgs(lineCode)
      .returns(wmataStationInformationMock);
```

This is creating a stub for the `wmata` service’s method `stationInformation` with args `lineCode` that will return the mock at `wmataStationInformationMock`.

This lets you build out basic stubs so that the test runner will use your stubs in lieu of methods it runs across. This is good because you can isolate behavior.

Sinon can do a lot more than just stubs.

For more on testing with sinon, I recommend reading [How to best use Sinon with Chai](https://blog.logrocket.com/sinon-with-chai/) by Leighton Wallace.

### Demo

Before I dive into actually building tests, I want to give a brief description of my project.

ms-starwars is actually an orchestration of API calls to the Star Wars API (SWAPI), which is [available here](https://swapi.co/). SWAPI is a very good API unto itself, and provides a wealth of data on a large part of the Star Wars cannon.

What’s even cooler is that SWAPI is community-driven. So if you see somewhere missing information, you can open a PR to their [project here](https://github.com/phalt/swapi) and add it yourself.

When you call endpoints for SWAPI, the API returns additional endpoints you can call to get more information. This makes the rest calls somewhat lightweight.

Here’s a response from the `film` endpoint:

```jsx
{
    "title": "A New Hope",
    "episode_id": 4,
    "opening_crawl": "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....",
    "director": "George Lucas",
    "producer": "Gary Kurtz, Rick McCallum",
    "release_date": "1977-05-25",
    "characters": [
        "https://swapi.co/api/people/1/",
        "https://swapi.co/api/people/2/",
        "https://swapi.co/api/people/3/",
        "https://swapi.co/api/people/4/",
        "https://swapi.co/api/people/5/",
        "https://swapi.co/api/people/6/",
        "https://swapi.co/api/people/7/",
        "https://swapi.co/api/people/8/",
        "https://swapi.co/api/people/9/",
        "https://swapi.co/api/people/10/",
        "https://swapi.co/api/people/12/",
        "https://swapi.co/api/people/13/",
        "https://swapi.co/api/people/14/",
        "https://swapi.co/api/people/15/",
        "https://swapi.co/api/people/16/",
        "https://swapi.co/api/people/18/",
        "https://swapi.co/api/people/19/",
        "https://swapi.co/api/people/81/"
    ],
    "planets": [
        "https://swapi.co/api/planets/2/",
        "https://swapi.co/api/planets/3/",
        "https://swapi.co/api/planets/1/"
    ],
    "starships": [
        "https://swapi.co/api/starships/2/",
        "https://swapi.co/api/starships/3/",
        "https://swapi.co/api/starships/5/",
        "https://swapi.co/api/starships/9/",
        "https://swapi.co/api/starships/10/",
        "https://swapi.co/api/starships/11/",
        "https://swapi.co/api/starships/12/",
        "https://swapi.co/api/starships/13/"
    ],
    "vehicles": [
        "https://swapi.co/api/vehicles/4/",
        "https://swapi.co/api/vehicles/6/",
        "https://swapi.co/api/vehicles/7/",
        "https://swapi.co/api/vehicles/8/"
    ],
    "species": [
        "https://swapi.co/api/species/5/",
        "https://swapi.co/api/species/3/",
        "https://swapi.co/api/species/2/",
        "https://swapi.co/api/species/1/",
        "https://swapi.co/api/species/4/"
    ],
    "created": "2014-12-10T14:23:31.880000Z",
    "edited": "2015-04-11T09:46:52.774897Z",
    "url": "https://swapi.co/api/films/1/"
}
```

Additional API endpoints are returned for various areas including characters, planets, etc.

To get all the data about a specific film, you’d have to call:

- the film endpoint
- all endpoints for characters
- all endpoints for planets
- all endpoints for starships
- all endpoints for vehicles
- all endpoints for species

I built ms-starwars as an attempt to bundle HTTP calls to the returned endpoints, and enable you to make single requests and get associated data for any of the endpoints.

To setup this orchestration, I created Express.js routes and associated controllers.

I also added a cache mechanism for each of the SWAPI calls. This boosted my APIs performance so that these bundled HTTP calls don’t have the latency associated with making multiple HTTP calls, etc.

Within the project, the unit tests are available at `/test/unit`. The `integration` tests are available at `test/integration`. You can run them with my project’s npm scripts:  
`npm run unit-tests` and `npm run intergration-tests`.

In the next sections, we’ll walk through writing unit and integration tests. Then we’ll cover some considerations and optimizations you can make.

Let’s get to the code.

### Unit tests

First, let’s create a new file in the sample project at `/test/firstUnit.js`

At the top of your test, let’s add the following:

```jsx
const sinon = require('sinon');
const chai = require('chai');
const expect = chai.expect;
const swapi = require('../apis/swapi');
const starwars = require('../controllers/starwars');
// swapi mocks
const swapiFilmListMock = require('../mocks/swapi/film_list.json');
// starwars mocks
const starwarsFilmListMock = require('../mocks/starwars/film_list.json');
```

What’s this doing? Well, the first several lines are pulling in the project’s dependencies:

```jsx
const sinon = require('sinon');
const chai = require('chai');
const expect = chai.expect;
const swapi = require('../apis/swapi');
const starwars = require('../controllers/starwars');
```

- Pulling in the [sinon framework](https://sinonjs.org/).
- Pulling in the [chai framework](https://www.chaijs.com/).
- Defining `expect` so we can use it assertions.
- Pulling in the `swapi` api service that are defined in the project. These are direct calls to the SWAPI endpoints.
- Pulling in the `starwars` api controllers that are defined in the project. These are orchestration of the SWAPI endpoints.

Next, you’ll notice all the mocks pulled in:

```jsx
// swapi mocks
const swapiFilmListMock = require('../mocks/swapi/film_list.json');
// starwars mocks
const starwarsFilmListMock = require('../mocks/starwars/film_list.json');
```

These are JSON responses from both the SWAPI endpoints and results returned from the project’s controllers.

Since our unit tests are just testing our actual code and not dependent on the actual flows, mocking data enables us to just test the code without relying on the running services.

Next, let’s define our first test with the following:

```jsx
describe('Film List', function() {
  afterEach(function() {
    swapi.films.restore();
  });
  it('should return all the star wars films when called', async function() {
    sinon.stub(swapi, 'films').returns(swapiFilmListMock);
    const response = await starwars.filmList();
    expect(response).to.deep.equal(starwarsFilmListMock);
  });
});
```

Here, the `describe` block is defining an occurrence of the test.

You would normally use `describe` and wrap that with an `it` . This enables you to group tests so that `describe` can be thought of as a name for the group and `it` can be thought of as the individual tests that will be run.

You’ll also notice we have an `afterEach` function.

There are several of these type of functions that work with Mocha.

Typically, the one’s you’ll see most often are `afterEach` and `beforeEach`. These are basically lifecycle hooks that enable you to setup data for a test, and then free resources after a test is run.

There’s a `swapi.films.restore()` call within the `afterEach`.

This frees up the SWAPI `films` endpoint for stubbing and future tests. This is necessary since the starwars controller that I’m testing is calling the SWAPI `films` endpoint.

In the `it` block, you’ll notice there is a definition followed by an `async function` call. The `async` call here indicates to the runner that there is asynchronous behavior to be tested. This enables us to use the `await` call that you see in line 7.

Finally, we get to the test itself.

First, we define a stub with:

```jsx
sinon.stub(swapi, 'films').returns(swapiFilmListMock);
```

This stub signals to Mocha to use the mock file whenever the `films` method is called from the `swapis` API service.

To free up this method in your test runner, you’ll need to call the `restore`.

This isn’t really a problem for us here since we’re just running one test, but if you had many tests defined then you’d want to do this. I’ve included it here just to indicate convention.

Finally, we have our actual method call and an `expect` to check the result:

```jsx
const response = await starwars.filmList();
expect(response).to.deep.equal(starwarsFilmListMock);
```

When you run this test, it should call the `filmList` controller, and return what would be expected with the `starwarsFilmListMock` response.

Let’s run it.

Install Mocha globally in your terminal with:

```jsx
npm i mocha --global
```

Then, run the test with:

```jsx
mocha test/firstUnit
```

You should see the following:

![ms-starwars](https://blog.logrocket.com/wp-content/uploads/2019/11/ms-starwars-nocdn.png)

On a high level, this is what you can expect with any unit tests.

Notice that we did the following:

1. Arrange – we set up our data by creating a stub
2. Act – we made a call to our controller method to act on the test
3. Assert – we asserted that the response from the controller equals our saved mock value

This pattern of Arrange, Act, and Assert is a good thing to keep in mind when running any test.

### A more complicated unit test

This first test showed you the basic set-up — you now have a basic understanding of arrange, act, and assert.

Let’s consider a more complicated test:

```jsx
describe('Film', function() {
  afterEach(function() {
    swapi.film.restore();
    swapi.people.restore();
  });
  it('should return all the metadata for a film when called', async function() {
    const filmId = '1';
    const peopleId = '1';
    const planetId = '1';
    const starshipId = '2';
    const vehicleId = '4';
    const speciesId = '1';
    sinon
      .stub(swapi, 'film')
      .withArgs(filmId)
      .resolves(swapiFilmMock);
    sinon
      .stub(swapi, 'people')
      .withArgs(peopleId)
      .resolves(swapiPeopleMock);
    sinon
      .stub(swapi, 'planet')
      .withArgs(planetId)
      .resolves(swapiPlanetMock);
    sinon
      .stub(swapi, 'starship')
      .withArgs(starshipId)
      .resolves(swapiStarshipMock);
    sinon
      .stub(swapi, 'vehicle')
      .withArgs(vehicleId)
      .resolves(swapiVehicleMock);
    sinon
      .stub(swapi, 'species')
      .withArgs(speciesId)
      .resolves(swapiSpeciesMock);
    const response = await starwars.film(filmId);
    expect(response).to.deep.equal(starwarsFilmMock);
  });
});
```

Wow, that’s a lot of stubs! But it’s not as scary as it looks — this test basically does the same thing as our previous example.

I wanted to highlight this test because it uses multiple stubs (with args).

As I mentioned before, ms-starwars bundles several HTTP calls under the hood. The one call to the `film` endpoint actually makes calls to `film`, `people`, `planet`, `starship`, `vehicle`, and `species`. All of these mocks are necessary to do this.

Generally speaking, this is what your unit tests will look like. You can do similar behaviors for PUT, POST, and DELETE method calls.

The key is to be testing code. Notice that we made use of a stub and mock in our return value.

We were testing the application logic, and not concerned with the application working in its entirety. Tests that test full flows are typically integration or end-to-end tests.

### Integration tests

For the unit tests, we were just focused on testing the code itself without being concerned with end-to-end flows.

We were only focused on making sure the application methods have the expected outputs from the expected input.

With integration tests (and also for end-to-end tests), we’re testing flows.

Integration tests are important because they make sure individual components of your application are able to work together.

This is important with microservices because you’ll have different classes defined that (together) create a microservice.

You might also have a single project with multiple services, and you’d write integration tests to make sure they work well together.

For the ms-starwars project, we’re just going to make sure that the orchestration provided by the controllers work with the individual API calls to the SWAPI endpoints.

Go ahead and define a new file with `/test/firstIntegration.js`.

Add the following to the top of the file:

```jsx
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
const app = require('../server');
const should = chai.should();
const expect = chai.expect;
// starwars mocks
const starwarsFilmListMock = require('../mocks/starwars/film_list.json');
```

What is this doing?

First, we’re defining an instance of [chai](https://www.chaijs.com/) and [chai-http](https://www.chaijs.com/plugins/chai-http/). Next, we’re defining an instance of the actual app itself from the `server.js` file.

Then we’re pulling in should and expect, and finally we’re pulling in a mock that we’re going to use to compare the response.

Let’s build our test:

```jsx
describe('GET /films-list', () => {
  it('should return a list of films when called', done => {
    chai
      .request(app)
      .get('/films-list')
      .end((err, res) => {
        res.should.have.status(200);
        expect(res.body).to.deep.equal(starwarsFilmListMock);
        done();
      });
  });
});
```

So what’s this doing?

Well, this is similar to the syntax we saw before — we have the `describe` with an `it`. This sets up the test and indicates that the test is actually occurring here.

Then we make a call to `chai.request` and pass our reference to our app (`server.js`) file. This is how we can engage the [chai-http](https://www.chaijs.com/plugins/chai-http/) library to make our HTTP call.

We then are passing a GET call to the `films-list` endpoint from our API.

Then we call `end` to signal behavior on what to do when the call completes.

We expect a status of `200` with:

```jsx
res.should.have.status(200);
```

Then we expect a body to equal our mock with:

```jsx
expect(res.body).to.deep.equal(starwarsFilmListMock);
```

Finally, we call `done()` to stop the test runner.

The really cool part about this is that it starts your application locally, runs the request you specify (GET, POST PUT DELETE, etc.), enables you to capture the response, and brings down the local running application.

So now with our integration test set up, run it with the following:

```jsx
    mocha --exit test/firstIntegration
> note that the `--exit` flag is being passed here just to signal to the test runner to stop after the test finishes.  You can run it without `--exit` , but it would just wait for you to manually cancel the process.
```

Then you should see something like this:

![git-master-nocdn.png](https://blog.logrocket.com/wp-content/uploads/2019/11/git-master-nocdn.png)

There are other frameworks that can literally run your application beside your test runner.

However, using chai-http is clean and easy to implement with any of your projects and doesn’t require additional frameworks in general.

I recommend playing with the chai-http library and your application, and consulting the documentation when you have questions.

### Testing strategies

With any test suite, we should also consider an overall strategy. You should ask yourself, what do you want to test? Have you covered all the application flows? Are there specific edge conditions that you want to test? Do you need to provide reports for your Product Owner or Team Lead?

The frameworks I’ve covered so far enable you to run tests, but there are many options for test reporters. Additionally, there are several test tools out there that provide code coverage.

> If you consult the [post here](https://medium.com/walkme-engineering/measure-your-nodejs-code-coverage-using-istanbul-82b129c81ae9), you’ll see a good example of the [Istanbul Test Coverage Tool](https://istanbul.js.org/).

One of the failures that I’ve experienced with teams is that they think if the code coverage tool says you’ve got 90% coverage, then you’re good. This isn’t really accurate.

When you write your tests, you should consider odd behavior and testing for specific inputs. Just because your code has been covered doesn’t mean that the outliers and edge cases have been covered.

With any test suite, you should consider not just the “happy path” and “sad path” scenarios, but also edge cases and specific cases to your customers.

Additionally, there are often times with integration and end-to-end tests that you may be reliant on external HTTP calls.

This could be problematic if the external APIs are down.

I actually recently built another microservice that did just that. I employed a mock server to run my tests, and used [start-server-and-test](https://www.npmjs.com/package/start-server-and-test) to run both together.

This proved to be a great experience because I could run my tests in isolation, and it freed me up from relying on the external APIs.

I recommend [checking out my article here](https://dev.to/andrewevans0102/mock-servers-with-integration-tests-52de). This is a great example of an innovative approach to testing without dependencies.

Overall, your test strategy is going to be based on your situation. I recommend you look beyond just “happy path” or “expected cases” and consider everything else.

### Conclusion

I hope my post here has given you a good introduction to testing your Node.js applications.

We’ve discussed the different frameworks and technologies you can use in your Node.js applications. We’ve also walked through unit and integration tests for your Node.js applications.

The framework I used here was Express.js, but these patterns could apply to other Node.js frameworks as well. I recommend checking out the links I’ve provided above, as well as the documentation for each framework.

Follow me on twitter at [@AndrewEvans0102.](https://twitter.com/andrewevans0102)

* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/unit-and-integration-testing-for-node-js-apps/).

## 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 and integration testing for Node.js apps](https://blog.logrocket.com/unit-and-integration-testing-for-node-js-apps/) appeared first on [LogRocket Blog](https://blog.logrocket.com).