Testing is a crucial part of software development. It helps ensure that your code works as expected, catches bugs early in the development process, and provides confidence in the stability of your application. For Node.js and Express.js developers, Jest is a popular testing framework that makes testing easy and effective.
In this article, I'll dive into using Jest to test an Express.js API. I'll cover key concepts and walk through practical examples to help you master Jest for your projects.
Setting up the Environment
Before diving into Jest, let's set up our environment. We assume you already have an Express.js application in place. If not, you can quickly scaffold one with express-generator
or set up a minimal Express app manually.
You can see how to set up an Express.js application with express-generator
here
I'll use the below project structure as an example.
my-express-app/
├── src/
│ └── middleware/
│ │ └── validateSignupRequestBody.js
│ └── app.js
├── __tests__/
│ └── validateSignupRequestBody.test.js
├── jest.setup.js
├── package.json
└── ...
We have an Express app with a middleware (validateSignupRequestBody.js
) that validates the request body of a signup endpoint. Our goal is to test this middleware using Jest.
We will be testing the code in validateSignupRequestBody.js
const Joi = require('joi');
const requestBodySchema = Joi.object({
name: Joi.string().min(4).required(),
email: Joi.string().email().custom((value, helpers) => {
if (!value.endsWith('@gmail.com')) {
return helpers.error('any.invalid');
}
return value;
}, 'Email Validation').required(),
password: Joi.string().min(5).required(),
});
const validateSignupRequestBody = (req, res, next) => {
try {
const {error} = requestBodySchema.validate(req.body);
if(error) {
res.status(400).json({ message: "Invalid Request!", error: error.details[0].message });
return;
}
next();
} catch (error) {
next(error);
}
}
module.exports = validateSignupRequestBody;
Installing Jest
To get started, let's install Jest as a development dependency:
npm install --save-dev jest supertest @types/jest ts-jest @types/supertest
We've also added supertest
and TypeScript-related dependencies because our example assumes you are working with TypeScript. If you are using plain JavaScript, you can skip the TypeScript dependencies.
Configuring Jest
module.exports = {
testEnvironment: 'node',
transform: {
'^.+\\.ts$': 'ts-jest',
},
setupFilesAfterEnv: ['./jest.setup.js'],
};
This configuration tells Jest to use the ts-jest
transformer for TypeScript files and to run the jest.setup.js
script before each test suite. You can replace .ts
with .js
if you are using JavaScript.
Setting up a MongoDB Test Database
Many Express applications use databases like MongoDB. When testing, it's essential to isolate your tests from the production database. We'll use mongodb-memory-server
to create an in-memory MongoDB instance for testing.
In the jest.setup.js
file, add the following code:
const { MongoMemoryServer } = require('mongodb-memory-server');
const mongoose = require('mongoose');
let mongoServer;
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const uri = mongoServer.getUri();
process.env.DATABASE_URL = uri;
await mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
});
afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
global.setupMongoDB = () => {
// No need to redefine beforeAll and afterAll hooks here.
};
This code sets up an in-memory MongoDB server before your tests and shuts it down after testing is complete. It also exports a setupMongoDB
function that you can use in your test files.
Writing Tests
Now that we have our environment set up, let's write some tests using Jest. We'll focus on testing the validateSignupRequestBody
middleware. Create a test file named validateSignupRequestBody.test.ts
(or .js
for JavaScript) in the __tests__
directory with the following content:
const request = require('supertest');
const express = require('express');
const validateSignupRequestBody = require('path_to_the_validateSignupRequestBody.js')
const app = express();
// Middleware for parsing JSON request bodies
app.use(express.json());
// Register your middleware
app.use('/signup', validateSignupRequestBody);
describe('validateSignupRequestBody middleware', () => {
// Use the global MongoDB setup function
global.setupMongoDB();
it('should return 400 Bad Request if the request body is missing required fields', async () => {
const response = await request(app)
.post('/signup')
.send({});
expect(response.status).toBe(400);
expect(response.body).toMatchObject({
message: 'Invalid Request!',
error: expect.stringContaining('"name" is required'),
});
});
it('should return 400 Bad Request if the email format is invalid', async () => {
const response = await request(app)
.post('/signup')
.send({
name: 'John',
email: 'invalidemail',
password: 'password123',
});
expect(response.status).toBe(400);
expect(response.body).toMatchObject({
message: 'Invalid Request!',
error: expect.stringContaining('"email" must be a valid email'),
});
});
it('should return 400 Bad Request if the email domain is not @gmail.com', async () => {
const response = await request(app)
.post('/signup')
.send({
name: 'John',
email: 'example@yahoo.com',
password: 'password123',
});
expect(response.status).toBe(400);
expect(response.body).toMatchObject({
message: 'Invalid Request!',
error: expect.stringContaining('"email" contains an invalid value'),
});
});
});
In this test file:
We create an Express application and apply the
express.json()
middleware for parsing JSON request bodies.We register the
validateSignupRequestBody
middleware on the/signup
route.We use Jest's
describe
andit
functions to structure our tests.We use
supertest
to make HTTP requests to our Express app and assert the responses.We utilize the
global.setupMongoDB()
function to set up and tear down an in-memory MongoDB server for our tests.
Running Tests
With everything set up and tests written, you can now run your tests using Jest:
npm test
Jest will execute your tests, and you should see the test results in your terminal.
Conclusion
Testing your Express.js API with Jest is a powerful way to ensure your application behaves as expected. In this article, we've covered the essential steps to set up Jest for testing Express.js applications, including configuring Jest, setting up an in-memory MongoDB test database, and writing test cases.
With Jest, you can confidently test your middleware, route handlers, and other parts of your Express.js application, helping you catch and fix issues early in the development process. Whether you're building a small API or a large-scale application, mastering Jest for testing is a valuable skill that can lead to more reliable and maintainable code.