One of the challenges I faced during backend testing was how to mock Mongoose models. At first, I tried to mock the whole model class, but this approach became messy fast, especially when using Typescript. But after extensive research, I found a library, "mongodb-memory-server", that solves this problem elegantly.
The idea is to spin a fake in-memory database that will be used during testing, clear it, and shut it down after all tests are complete. The first step is to create functions for connection to the "fake" database:
import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
let mongodb: MongoMemoryServer;
/* Connect to DB */
export const connect = async () => {
mongodb = await MongoMemoryServer.create();
const uri = mongodb.getUri();
await mongoose.connect(uri, {});
};
/* Close db connection */
export const closeDatabase = async () => {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
await mongodb.stop();
};
/* Delete db collections */
export const clearDatabase = async () => {
const collections = mongoose.connection.collections;
for (const key in collections) {
const collection = collections[key];
await collection.deleteMany({});
}
};
And then we use Jest functions to establish a connection, clear the database, and close the connection:
import * as dbHandler from "./db";
/* create connection to fake db */
beforeAll(async () => {
await dbHandler.connect();
});
/* depends if you want to carry over information from
one test to another */
afterEach(async () => {
await dbHandler.clearDatabase();
});
/* close connection after tests are done */
afterAll(async () => {
await dbHandler.closeDatabase();
});
After these two simple steps, we can use Mongoose models as we would in a regular database:
describe("getAllArticles", () => {
test("no projects found", async () => {
const response = await request(app).get("/api/article/project");
expect(response.status).toBe(404);
expect(response.body.msg).toStrictEqual("No projects found");
});
});