Guide to writing integration tests in express js with Jest and Supertest
Adeku Ali
Posted on March 7, 2023
Writing integration tests for new developers can prove a little tricky. During my first foray into writing integration tests, it took a while to understand how to write tests for express applications effectively.
While unit testing is a way of testing a unit - the smallest piece of code that can be logically isolated in a system, integration testing is testing all solid features that make up the application.
In this article, I illustrate how to write integration tests for a task manager API built with express.js.
Repository
Check out the complete source code here.
Prerequisites
- Basic Knowledge of Javascript
- Node installed on your device
Setting up your Test Folder Structure
In the root directory of your application, create a folder named test
; in your test folder, create a folder called integration
, and this folder should contain all your integration test files.
Integration test files should be named like this .spec.js
or .test.js
, so your test filenames should look like foo.spec.js
or foo.test.js
Configuring Jest
You are going to make use of Jest as the testing framework. Jest is a JavaScript testing framework designed to ensure the correctness of any JavaScript codebase. It allows you to write tests with an approachable, familiar, and feature-rich API that gives you results quickly.
npm install jest --save-dev
In the package.json
file, add a test
script with the following command:
"test": "cross-env NODE_ENV=test jest --testTimeout=5000 --detectOpenHandles"
Jest runs in a browser-like environment using jsdom
by default, but since this is a node application, a node-like environment is specified instead.
"jest": {"test environment": "node"}
Creating a Mock Database
You can create a mock database to avoid writing tests that interfere with the database. For this article, since MongoDB is used as the database, you will use MongoMemoryServer as your mock database.
MongoMemoryServer will spin up a real MongoDB server and allow storing test data in memory.
npm i mongodb-memory-server --save-dev
In your test folder, create a boostrap.js
file; this should contain your mock database configuration.
test/bootstrap.js
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
let mongod;
beforeAll(async () => {
mongod = await MongoMemoryServer.create();
const uri = mongod.getUri();
await mongoose.connect(uri);
});
afterAll(async () => {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
await mongod.stop();
});
afterEach(async () => {
const collections = mongoose.connection.collections;
for (const key in collections) {
const collection = collections[key];g,
await collection.deleteMany();
}
});
Writing The Tests
To test the API endpoints of the application, you will import the instance of the application, the supertest
module, and the task Model. The app.js
file looks like this :
npm i supertest
app.js
const express = require('express')
const taskRouter = require("./routes/task");
const notFound = require("./middlewares/not-found")
const errorHandler = require("./middlewares/error-handler")
const app = express()
const morgan = require('morgan')
app.use(express.static("./public"));
app.use(express.json());
app.use(morgan('dev'))
app.use("/api/v1/tasks", taskRouter);
app.use(notFound)
module.exports = app
This file contains the tests for the API routes in the application.
test/integration/tasksController.test.js
const request = require("supertest");
const app = require("../../app");
const TaskModel = require("../../models/tasks");
describe("Task Controller", () => {
describe("create task", () => {
it("should return 201 and the task created", async () => {
const response = await request(app)
.post("/api/v1/tasks")
.set("content-type", "application/json")
.send({
name: "task testing",
completed: "true",
});
expect(response.status).toBe(201);
expect(response.body).toHaveProperty("tasks");
});
});
describe("get all tasks", () => {
it("should return 200 and all tasks", async () => {
const response = await request(app)
.get("/api/v1/tasks")
.set("content-type", "application/json");
expect(response.status).toBe(200);
expect(response.body).toHaveProperty("tasks");
});
});
describe("get a task", () => {
let task;
beforeEach(async () => {
task = await TaskModel.create({
name: "task testid",
});
});
it("should return 200 and a single task", async () => {
const response = await request(app)
.get(`/api/v1/tasks/${task.id}`)
.set("content-type", "application/json");
expect(response.status).toBe(200);
expect(response.body).toHaveProperty("task");
});
});
describe("update a task", () => {
let task;
beforeEach(async () => {
task = await TaskModel.create({
name: "task2 testid",
});
});
it("should return 404 if the task with the id doesnt exist", async () => {
const taskId = "639c80ef98284bfdf111ad09";
const response = await request(app).patch(`/api/v1/tasks/${taskId}`);
expect(response.status).toBe(404);
expect(response.body.msg).toEqual("this task does not exist");
});
it("should return 200 and the updated task", async () => {
const response = await request(app)
.patch(`/api/v1/tasks/${task.id}`)
.send({ name: "newtask" });
expect(response.status).toBe(200);
expect(response.body).toHaveProperty("task");
});
});
describe("delete a task", () => {
let task;
beforeEach(async () => {
task = await TaskModel.create({
name: "task2 testid",
});
});
it("should return 404 if the task with the id doesnt exist", async () => {
const taskId = "639c80ef98284bfdf111ad09";
const response = await request(app).delete(`/api/v1/tasks/${taskId}`);
expect(response.status).toBe(404);
expect(response.body.msg).toEqual("this task does not exist");
});
it("should return 200 and the deleted task", async () => {
const response = await request(app).delete(`/api/v1/tasks/${task.id}`);
expect(response.status).toBe(200);
expect(response.body).toHaveProperty("task");
});
});
});
Conclusion
Testing is an art, and the knowledge to write good tests for your application can act as a first line of defence for your application.
In this article, you looked at how to write integration tests for an express application using Jest, Supertest, and mongodb-memory-server, configuring jest, and also creating and configuring your test mock database.
Resources
Posted on March 7, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 12, 2024
December 22, 2023