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 JavaScript runtime environment outside of browser
- Express Backend application framework for Node.js
- MongoDB NoSQL database like JSON.
- Jest JavsScript testing framework maintained by Facebook
- supertest npm package that helps test HTTP
Writing tests
Steps
- Prepare a mongodb in memory server for testing
- Write tests with jest and supertest
- (Optional) Set up
NODE_ENV
totest
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
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
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
"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
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!
Top comments (0)