Testing Node.js/Express app + MongoDB with jest and supertest
Tags: node, mongodb, jest
Published: 2021-11-15 21:35:18

# Introduction
I find it quite hard to find the right steps when you already have a set of technologies in your project, and as the title goes, my target audience are those who already know how to develop a backend application in Express + MongoDB but not how to write tests for it. If you are still with me, let's get started. 

## Tech Stack
- [Node.js](https://nodejs.dev/)
JavaScript runtime environment outside of browser
- [Express](https://expressjs.com/)
Backend application framework for Node.js
- [MongoDB](https://www.mongodb.com/)
NoSQL database like JSON.
- [Jest](https://jestjs.io/)
JavsScript testing framework maintained by Facebook
- [supertest](https://github.com/visionmedia/supertest)
npm package that helps test HTTP

# Writing tests

## Steps
1. Prepare a mongodb in memory server for testing
2. Write tests with jest and supertest
3. (Optional) Set up `NODE_ENV` to `test`

## Prepare a mongodb in memory server for testing

First, install the in-memory-mongodb-server with the command below.
`npm i -D mongodb-memory-server`

> I put all test files inside `__test__` folder but feel free to modify the path as needed.

`/__tests__/config/database.js`
```javascript
import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
import { MongoClient } from "mongodb";

let connection: MongoClient;
let mongoServer: MongoMemoryServer;

const connect = async () => {
  mongoServer = await MongoMemoryServer.create();
  connection = await MongoClient.connect(mongoServer.getUri(), {});
};

const close = async () => {
  await mongoose.connection.dropDatabase();
  await mongoose.connection.close();
  await mongoServer.stop();
};

const clear = async () => {
  const collections = mongoose.connection.collections;
  for (const key in collections) {
    await collections[key].deleteMany({});
  }
};
export default { connect, close, clear };
```

As you do with ordinary MongoDB, you connect to the database before running tests and you close connection after running tests. You can also nuke the data in database using clear. I use default export here to import the module as `db` and use the functions like `db.connect()` or `db.clear()`, but it is totally up to you or TypeScript settings.

## Write tests with jest and supertest

I assume most of you already have installed the testing dependencies, but if not, please run the command below.
`npm i -D jest supertest`

```javascript
import request from "supertest";
import app from "../src/index";
import db from "./config/database";

const agent = request.agent(app);

beforeAll(async () => await db.connect());
afterEach(async () => await db.clear());
afterAll(async () => await db.close());

describe("tags", () => {
  describe("POST /tags", () => {
    test("successful", async () => {
      const res = await agent.post("/tags").send({ name: "test-tag"});
      expect(res.statusCode).toEqual(201);
      expect(res.body).toBeTruthy();
    });
  });
});
```

As mentioned in the previous step, you can make use of `beforeAll`, `afterEach`, and `afterAll` hooks for the database connections/modifications. If you want to keep the data that you create with POST, you can remove `db.clear()` from `afterEach` hook so you can interact with the same object for other methods like `PUT` or `DELETE`.

## Set up `NODE_ENV` to `test`

For better maintanance, I have passed `NODE_ENV=test` just before tests.

`package.json`
```json
"scripts": {
  "test": "export NODE_ENV=test && jest --forceExit --runInBand",
}
```

In order to avoid port collision, my express app will not occupy the port while testing. And I use `dotenv` for dealing with environment variables for those who aren't familiar with this.

`/src/index.ts`
```javascript
if (process.env.NODE_ENV !== "test") {
  app.listen(port, () => {
    console.log(`Express app listening at ${process.env.BASE_URI}:${port}`);
  });
}
```

# Conclusion

In the end, it is all about the database setup for testing. And I hope this post was right for you.

Feel free to reach out if you have any questions or suggestions to make this article better. Thank you for reading. Happy Coding!