Mock Servers with Integration Tests
Tags: javascript, testing
Published: 2019-10-08 14:52:29



Recently I built a microservice and needed to build out some integration tests.  In the process of building the tests, I realized that my microservice was reliant upon some external APIs that needed to be up for my tests to pass.  Subsequently, I realized I could use a Mock Server for the external API calls, and as such keep my testing siloed within my application.

This post is going to share some of the things I learned, and how I used a Mock Server for Integration Tests.

## My Microservice

So before going into testing, I wanted to explain what my microservice is and what it tests.  

Living near the DC Metro area, I commonly take the metro trains into work everday.  Their schedules can be somewhat intermittent, and I was looking for a way to be able to plan my day.

The Washington Metropolitan Area Transit Authority (WMATA) has a set of APIs that are publicly accessible.  You can use their endpoints for things like arrival times, station information, and the like.  I wanted get specific information from those APIs, and thought it would be fun to write an orchestration service that calls the APIs and returns the data in a format for a frontend application to consume.

So I created __ms-metro__.

__ms-metro__ is open source and can be [viewed here](https://github.com/andrewevans0102/ms-metro).

The API has 5 main endpoints that include the following:
- `/station-list`
- `/stationInformation`
- `/station-hours`
- `/arrival-times`
- `/station-route`

## My Test Strategy

So when I wrote out my Integration Tests, I used Mocha and Chai.  I decided to use [chai-http](https://www.npmjs.com/package/chai-http) as it had a nice way of handling direct requests to my running application.

This was all great, but I still faced the challenge of the WMATA API calls that my app was doing.

So I decided that I would use environment variables to determine when the API calls were made during Integration Tests.  Then when that happens, I would call a Mock Server in lieu of the actual HTTP call.

This looks like the following:
```javascript
const stationList = async LineCode => {
  if (process.env.MOCK_SERVER) {
    return mockServer('http://localhost:3000/station-list');
  }

  const options = {
    uri: 'https://api.wmata.com/Rail.svc/json/jStations',
    qs: {
      LineCode: LineCode
    },
    headers: {
      api_key: process.env.WMATA_API_SECRET_KEY
    },
    json: true
  };
  const response = await rp(options);
  const { Stations: stations } = response;
  return stations;
};
```

If you notice here I am checking for the environment variable `MOCK_SERVER`.

The method `mockServer` then looks like the following:
```javascript
const mockServer = async jsonLocation => {
  const options = {
    uri: jsonLocation,
    json: true
  };
  return rp(options);
};
```

I also, make use of additional environment variables in my Integration Testing to specify to the `json-server` specific payloads for specific tests.  An example of this looks like the following:
```javascript
const nextTrains = stationCode => {
  if (process.env.MOCK_SERVER) {
    // here an environment variable is used to determine which value the mock server retrieves
    if (process.env.ARRIVAL_TIME === 'C02') {
      return mockServer('http://localhost:3000/C02-times');
    } else {
      return mockServer('http://localhost:3000/next-trains');
    }
  }

  const options = {
    uri:
      'https://api.wmata.com/StationPrediction.svc/json/GetPrediction/' +
      stationCode,
    headers: {
      api_key: process.env.WMATA_API_SECRET_KEY
    },
    json: true
  };

  return rp(options);
};
```


Wait, so that's all cool but what is this thing running on `http://localhost:3000/station-list`?  That's my Mock Server 😁😁.

## The Actual Mock Server

While my tests are running, I run an instance of [json-server](https://www.npmjs.com/package/json-server).  To setup `json-server` is fairly simple, you just have to install the npm package and then have a JSON file that is stored somewhere for it to read.  the `json-server` is super easy to work with because you just give it a path and it will parse the JSON file you specified in config and pull out the payload.  I encourage you to checkout their npm page for more info.

Additionally, I must note that the `json-server` documentation encourages you to install the npm pacakge globally.  This was not ideal since I wanted to run it in CI etc.  So I actually installed it within my project and the reference the package directly in an npm script.  This looks like the following:
```bash
./node_modules/.bin/json-server --watch ./mock-server/response.json,
```

Also, note that when I run my Integration Tests I set the environment variable with an npm script as follows:
```bash
MOCK_SERVER=true mocha --exit test/integration.js
```

Setting the environment variable in the npm script is nice because I don't have to remember to set it if I'm on a different computer etc.

So now this is all great, but wait, I need to run these two processes side by side.  How do I do this?  That's the next thing I'm going to discuss.

## Running Together

So up to this point, you've seen how I wrote my tests and how I stood up a Mock Server.  However, I need these things to run together when I test both locally and in CI.  The great part is, there are many utilities out there that will help you with this.  I chose [concurrently](https://www.npmjs.com/package/concurrently) and [start-server-and-test](https://www.npmjs.com/package/start-server-and-test).

I use __concurrently__ for my local testing, and then I use __start-server-and-test__ for my CI testing.

To run both of these is very intuitive.  You just pass the processes you want to run along with the address to search for.

local testing:
```bash
concurrently "npm run json-server" "npm run integration-tests"
```

CI testing:
```bash
start-server-and-test "npm run json-server" http://localhost:3000 "npm run integration-tests"
```

(note in the above I have npm scripts for the json-server and actually running the integtarion tests.  For more please consult my project's repo.)

## Wrapping Up

So as you've seen in this post, a Mock server is super nice because it contains your Integration Testing within your application.  You can even run these tests offline or in a closed network if you needed.  I encourage you to check out `json-server` as well as [my project](https://github.com/andrewevans0102/ms-metro) for more.

Feel free to leave comments or connect with me on Twitter at [@AndrewEvans0102](https://twitter.com/andrewevans0102).