Here’s how to set up unit tests for API endpoints.
We’ll be using the following NPM packages:
Express for the server (any other framework can be used)
Jest
Supertest
VS Code extension using:
Jest
Jest Runner
Installing dependencies
npm install express
npm install -D jest supertest
Here is what the directory structure looks like:
and lets not forget package.json
{
"name": "api-unit-testing",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest api.test.js",
"start":"node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.1"
},
"devDependencies": {
"jest": "^28.1.2",
"supertest": "^6.2.3"
}
}
Creating a simple express server
An express server that accepts POST
requests with a body
and title
on the /post
endpoint. If either one is missing, status 400 Bad Request
is returned.
// app.js
const express = require("express");
const app = express();
app.use(express.json());
app.post("/post", (req, res) => {
const { title, body } = req.body;
if (!title || !body)
return res.sendStatus(400).json({ error: "title and body is required" });
return res.sendStatus(200);
});
module.exports = app;
const app = require("./app");
const port = 3000;
app.listen(port, () => {
console.log(`http://localhost:${port} 🚀`);
});
The express app is exported for use with supertest
. The same can be done with other HTTP server frameworks.
Creating test file
// api.test.js
const supertest = require("supertest");
const app = require("./app"); // express app
describe("Creating post", () => {
// test cases goes here
});
Supertest
takes care of running the server and making requests for us, while we focus on testing.
The express app is passed to the supertest
agent and it binds express to a random available port. Then we can make an HTTP request to the desired API endpoint and compare its response status with our expectation:
const response = await supertest.agent(app).post("/post").send({
title: "Awesome post",
body: "Awesome post body",
});
We’ll create test for 3 cases:
- When both
title
andbody
are provided
test("creating new post when both title and body are provided", async () => {
const response = await supertest.agent(app).post("/post").send({
title: "Awesome post",
body: "Awesome post body",
});
// comparing response status code with expected status code
expect(response.statusCode).toBe(200);
});
- When either of them is missing
test("creating new post when either of the data is not provided", async () => {
const response = await supertest.agent(app).post("/post").send({
title: "Awesome post",
});
expect(response.statusCode).toBe(400);
});
- When both of them is missing
test("creating new post when no data is not provided", async () => {
const response = await supertest.agent(app).post("/post").send();
expect(response.statusCode).toBe(400);
});
and the final code should look like:
const supertest = require("supertest");
const app = require("./app"); // express app
describe("Creating post", () => {
test("creating new post when both title and body are provided", async () => {
const response = await supertest.agent(app).post("/post").send({
title: "Awesome post",
body: "Awesome post body",
});
expect(response.statusCode).toBe(200);
});
test("creating new post when either of the data is not provided", async () => {
const response = await supertest.agent(app).post("/post").send({
title: "Awesome post",
});
expect(response.statusCode).toBe(400);
});
test("creating new post when no data is not provided", async () => {
const response = await supertest.agent(app).post("/post").send();
expect(response.statusCode).toBe(400);
});
});
Running Test
Run test using following command:
npm run test
If using VS Code extensions like jest
and jest runner
then it’ll do the work for you.
And this is how we can easily test API endpoints. 💪