Testing an Express app with SuperTest, moxios and Jest
Tags: javascript, testing, express, node
Published: 2018-06-27 11:46:57

---
title: Testing an Express app with SuperTest, moxios and Jest
published: true
tags: javascript, testing, express, node
canonical_url: https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ijq4tzhu8yr0ph93umk8.jpg
---

Testing is a crucial part of the software development process.
It helps to catch bugs, avoid regressions and to document the behaviour of a piece of software.

Express is one of the most widespread libraries for building backend applications in JavaScript.
What follows is a summary of how to set up an efficient unit testing strategy for such an application as well as
a couple of situations you may be faced with when attempting to test.

Full code example can be found at https://github.com/HugoDF/express-supertest-moxios.

- [A simple Express app 🎁](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#a-simple-express-app)
  - [Testing strategy 🕵️‍](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#testing-strategy)
- [Testing a less simple Express app 📚](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#testing-a-less-simple-express-app)
  - [Testing strategy 🛠](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#testing-strategy)
  - [Where Redis gets mocked 🤡](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#where-redis-gets-mocked)
- [Parting thoughts 🦋](https://codewithhugo.com/testing-an-express-app-with-supertest-moxios-and-jest#parting-thoughts)


This was sent out on the [Code with Hugo newsletter](https://buttondown.email/hugo) on Monday.
[Subscribe](https://buttondown.email/hugo) to get the latest posts right in your inbox (before anyone else).


# A simple Express app 🎁

Say we have an Express set of route handlers like the following:

`hugo.js`:
```js
const {Router} = require('express');
const axios = require('axios');
module.exports = (router = new Router()) => {
  router.get('/hugo', async (req, res) => {
    const { data: userData } = await axios.get(
      'https://api.github.com/users/HugoDF'
    );
    const {
      blog,
      location,
      bio,
      public_repos,
    } = userData
    return res.json({
      blog,
      location,
      bio,
      publicRepos: public_repos,
    });
  });
  return router;
};
```

This would be consumed in a main `server.js` like so:

```js
const express = require('express');
const app = express();
const hugo = require('./hugo');

app.use(hugo());

app.listen(3000, () => {
  console.log(`Server listening on port 3000`);
});
```

For this to run, the following dependencies are required:
```js
npm i --save express axios
```

And it can be run using:
```js
node server.js
```

Hitting `/hugo` will return some JSON data pulled from my GitHub profile:

```
curl http://localhost:3000/hugo
{"blog":"https://codewithhugo.com","location":"London","bio":"Developer, JavaScript.","publicRepos":39}
```

## Testing strategy 🕵️‍

Testing is about defining some inputs and asserting on the outputs.

Now if we skip the chat about what a unit of test is, what we really care about with this API is that
when we hit `/hugo` we get the right response, using `jest` here's what a test suite might look like:

`hugo.test.js`
```js
const hugo = require('./hugo');
const express = require('express');
const moxios = require('moxios');
const request = require('supertest');

const initHugo = () => {
  const app = express();
  app.use(hugo());
  return app;
}

describe('GET /hugo', () => {
  beforeEach(() => {
    moxios.install();
  });
  afterEach(() => {
    moxios.uninstall();
  });
  test('It should fetch HugoDF from GitHub', async () => {
    moxios.stubRequest(/api.github.com\/users/, {
      status: 200,
      response: {
        blog: 'https://codewithhugo.com',
        location: 'London',
        bio: 'Developer, JavaScript',
        public_repos: 39,
      }
    });
    const app = initHugo();
    await request(app).get('/hugo');
    expect(moxios.requests.mostRecent().url).toBe('https://api.github.com/users/HugoDF');
  });
  test('It should 200 and return a transformed version of GitHub response', async () => {
    moxios.stubRequest(/api.github.com\/users/, {
      status: 200,
      response: {
        blog: 'https://codewithhugo.com',
        location: 'London',
        bio: 'Developer, JavaScript',
        public_repos: 39,
      }
    });
    const app = initHugo();
    const res = await request(app).get('/hugo');
    expect(res.body).toEqual({
      blog: 'https://codewithhugo.com',
        location: 'London',
        bio: 'Developer, JavaScript',
        publicRepos: 39,
    });
  });
});
```

To run the above, first of all, add the required dependencies:
```
npm install --save-dev jest moxios supertest
```

Run tests with:
```
npx jest
```

We're leveraging [SuperTest](https://github.com/visionmedia/supertest) and passing the express app to it.
SuperTest's fetch-like API is familiar and is `await`-able.

[moxios](https://github.com/axios/moxios) is a package to "mock axios requests for testing".
We're able to run our unit tests in watch mode without flooding the upstream REST API.
`moxios` needs to be installed and uninstalled, we do this before and after each test respectively.
This is to avoid an edge case where one failing test can make others fail due to moxios isn't torn down and re-set up properly if 
the error occurs before `moxios.uninstall` is called.

The `stubRequest` method should be passed 2 parameters:  

* The first is what is going to be intercepted, this can be a string (which will need to be a full URL),
or a Regular Expression.
* The second parameter is a response config object, the main keys we use are `status` and `response`.
Status will be the status in the `axios` fetch response and `response` will be the `data` in the `axios` fetch response.

# Testing a less simple Express app 📚

Let's say we have an app that's a blob store, backed by Redis (a simple key-value store amongst other things):

`blob-store.js`:
```js
const {Router} = require('router');

module.exports = (redisClient, router = new Router()) => {
  router.get('/store/:key', async (req, res) => {
    const { key } = req.params;
    const value = req.query;
    await redisClient.setAsync(key, JSON.stringify(value));
    return res.send('Success');
  });
  router.get('/:key', async (req, res) => {
    const { key } = req.params;
    const rawData = await redisClient.getAsync(key);
    return res.json(JSON.parse(rawData));
  });
  return router;
};
```

`server.js`:
```js
const express = require('express');
const app = express();

// For the sake of simplicity, 
// redisClient isn't in another module
const redis = require('redis');
const {promisify} = require('util');
const client = redis.createClient(process.env.REDIS_URL);

const redisClient = {
  getAsync: promisify(client.get).bind(client),
  setAsync: promisify(client.set).bind(client)
};

const hugo = require('./hugo');
const blobStore = require('./blob-store');

app.use(hugo());
app.use(blobStore(redisClient));

app.listen(3000, () => {
  console.log(`Server listening on port 3000`);
});
```

For a walkthrough of the example of the above, see [Setting up Express and Redis with Docker compose](/setting-up-express-and-redis-with-docker-compose#creating-a-blob-store).

To get it up and running:

- clone https://github.com/HugoDF/express-supertest-moxios
- have [Docker Community Edition](https://www.docker.com/community-edition) running
- run `docker-compose up`

Once the app is running, we can do the following:

1. Store some data: 
```
curl http://localhost:3000/store/my-key\?some\=value\&some-other\=other-value
Success
```
2. Retrieve that data:
```
curl http://localhost:3000/my-key
{
    "some": "value",
    "some-other": "other-value"
}
```

## Testing strategy 🛠

We have a decision to make here:

1. Mock Redis
2. Don't mock Redis

To not mock Redis would mean running a full Redis instance and setting up some test data before each test suite.
This means you're relying on some sort of ordering of tests and you can't parallelise without running multiple Redis instances to avoid data issues.

For unit(ish) tests, that we want to be running the whole time we're developing, this is an issue.
The alternative is to mock Redis, specifically, `redisClient`.

## Where Redis gets mocked 🤡

`blob-store.test.js`
```js
const blobStore = require('./blob-store');
const express = require('express');
const moxios = require('moxios');
const request = require('supertest');

const initBlobStore = (
  mockRedisClient = {
    getAsync: jest.fn(() => Promise.resolve()),
    setAsync: jest.fn(() => Promise.resolve())
  }
) => {
  const app = express();
  app.use(blobStore(mockRedisClient));
  return app;
}

describe('GET /store/:key with params', () => {
  test('It should call redisClient.setAsync with key route parameter as key and stringified params as value', async () => {
    const mockRedisClient = {
      setAsync: jest.fn(() => Promise.resolve())
    };
    const app = initBlobStore(mockRedisClient);
    await request(app).get('/store/my-key?hello=world&foo=bar');
    expect(mockRedisClient.setAsync).toHaveBeenCalledWith(
      'my-key',
      '{\"hello\":\"world\",\"foo\":\"bar\"}'
    );
  });
});

describe('GET /:key', () => {
  test('It should call redisClient.getAsync with key route parameter as key', async () => {
    const mockRedisClient = {
      getAsync: jest.fn(() => Promise.resolve('{}'))
    };
    const app = initBlobStore(mockRedisClient);
    await request(app).get('/my-key');
    expect(mockRedisClient.setAsync).toHaveBeenCalledWith(
      'my-key',
    );
  });
  test('It should return output of redisClient.getAsync with key route parameter as key', async () => {
    const mockRedisClient = {
      getAsync: jest.fn(() => Promise.resolve('{}'))
    };
    const app = initBlobStore(mockRedisClient);
    const response = await request(app).get('/my-key');
    expect(response.body).toEqual({});
  });
});
```

In short we set up our tests so we can pass an arbitrary `redisClient` object where we can mock the methods themselves.

# Parting thoughts 🦋

Testing an Express app is all about finding the boundary at which the mocking starts and where it stops.

This is an excercise in API design, how to test things in as large a unit as it makes sense to (eg. the whole endpoint),
without having to carry around the baggage of a full database/persistence layer.

For example, another approach to the Redis client tests would have been to create a mock client that maintains the
state somehow (ie writes to an object internally), and to inject/inspect that state (before and after the code under test respectively).

For the full code example, see https://github.com/HugoDF/express-supertest-moxios.

This was sent out on the [Code with Hugo newsletter](https://buttondown.email/hugo) on Monday.
[Subscribe](https://buttondown.email/hugo) to get the latest posts right in your inbox (before anyone else).

Cover photo [Bekir Dönmez](https://unsplash.com/photos/eofm5R5f9Kw?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/search/photos/clowns?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)