To write API tests for an Express app that uses MongoDB as the database, we are going to use a library called mongodb-memory-server. It spins up an in-memory instance of MongoDB, which is faster than running a separate MongoDB instance.
The library helps to give us a clean/empty database for each test case, so that the test cases do not interfere with each other (e.g. if a test case fail and leave some garbage data in its copy of database, that failure will not affect other test cases because each test case starts with a clean database).
Here is an example on using it with Jest: https://github.com/nodkz/mongodb-memory-server#simple-jest-test-example
In this example, we'll be adding tests to the express-books-api.
Install the testing libraries as dev dependencies:
npm install --save-dev jest supertest mongodb-memory-server
Create an app.test.js
file and initialize Mongodb Memory Server:
// app.test.js
const express = require("express");
const request = require("supertest");
// Initialize MongoDB Memory Server
const MongodbMemoryServer = require("mongodb-memory-server").default;
const mongod = new MongodbMemoryServer();
const mongoose = require("mongoose");
const Author = require("./models/author");
const app = require("./app");
async function addFakeAuthors() {
const author1 = new Author({
name: "paulo",
age: 49
});
await author1.save();
const author2 = new Author({
name: "john",
age: 50
});
await author2.save();
}
beforeAll(async () => {
// Increase timeout to allow MongoDB Memory Server to be donwloaded
// the first time
jest.setTimeout(120000);
const uri = await mongod.getConnectionString();
await mongoose.connect(uri);
});
afterAll(() => {
mongoose.disconnect();
mongod.stop();
});
beforeEach(async () => {
// Clean DB between test runs
mongoose.connection.db.dropDatabase();
// Add fake data to the DB to be used in the tests
await addFakeAuthors();
});
test("GET /authors", async () => {
const response = await request(app).get("/authors");
expect(response.status).toBe(200);
// Assert based on the fake data added
expect(response.body.length).toBe(2);
});
Each test should be independent of the others - importantly, the order in which the tests run shouldn't affect whether they pass or fail.
In the example above, we make use of the mongodb-memory-server
to automatically give us a fresh database in each test case.
Without that library, another possible solution is to explicitly delete all data in the test database after each test finishes running. That's a bit tedious but still works. Here is a sample project showing this approach. Checkout the test cases in tests/integration-tests
to see the sample tests.