Test-Driven Development (TDD) — Express Unit(API) Testing with Jest

Prince Gbenga Fabikun
3 min readJan 31, 2024

--

Introduction:

Testing is an integral part of creating reliable software and adopting a Test-Driven Development (TDD) approach can enhance the effectiveness of testing. TDD involves writing tests before implementing the actual code, fostering a design-driven development process. In this comprehensive guide, we’ll explore the TDD methodology while setting up and testing an Express API using the Jest testing framework. Through a step-by-step approach, we’ll focus on testing a UserController, starting from a failing test to the expansion of the code.

Definitions:

  • Express API: Express is a minimal and flexible Node.js web application framework widely used for building scalable server-side applications.
  • Jest: Jest is a JavaScript testing framework developed by Facebook, known for its simplicity and speed.
  • Supertest: Supertest is a library for testing HTTP assertions, commonly used with Jest for testing Express APIs.
  • Test-Driven Development (TDD): TDD is a development methodology where tests are written before the implementation code. The TDD cycle involves writing a failing test, implementing the minimum code to make the test pass, and then expanding and refining the code as needed.

Project Structure:

Organize your project to facilitate efficient TDD:

Step 1: Write a Failing Test

Begin the TDD cycle by writing a test that defines the desired functionality. In UserController.test.js:

// File: tests/controllers/UserController.test.js

const request = require('supertest');
const app = require('../../src/app');

describe('UserController', () => {
it('should fail to get a list of users', async () => {
const response = await request(app).get('/api/users');

// Expecting a 404 status code since the getUsers method is not implemented yet
expect(response.status).toBe(404);
// You can add more specific assertions based on your API's error handling
});

// Add more test cases for other UserController methods if needed
});

Step 2: Implement Minimum Code for Test Success

Write the minimum code necessary to make the failing test pass. In app.js:

// File: src/app.js

const express = require('express');
const UserController = require('./controllers/UserController');

const app = express();
const userController = new UserController();

// Implementing a basic route for /api/users
app.get('/api/users', userController.getUsers);

module.exports = app;
// File: src/controllers/UserController.js

class UserController {
getUsers(req, res) {
// Return static list of users
const users = [
{user 1 object},
{user 2 object}
];
res.status(200).json(users);
}
}

module.exports = UserController;

Step 3: Refactor and Expand Code

Refactor the code and expand its functionality while ensuring the tests continue to pass. In UserController.js:

// File: src/controllers/UserController.js

class UserController {
getUsers(req, res) {
// Fetch users (Assuming you have a function for this)
const users = getUsersFromDatabase();
res.status(200).json(users);
}
}

module.exports = UserController;

Now, you’ve successfully followed the TDD cycle: starting with a failing test, implementing the minimum code for success, and expanding the code while maintaining passing tests. This approach helps ensure that your application remains functional and reliable as it evolves.

Step 5: jest.config.js

Configure Jest with a jest.config.js file:

// File: jest.config.js

module.exports = {
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'],
};

Step 6: Run the Tests

Execute the Jest tests using the following command:

npm test

Conclusion:

Adopting Test-Driven Development with Jest in an Express API is a powerful way to build robust and maintainable applications. By writing tests before implementing code, you establish a clear understanding of expected behavior and catch potential issues early in the development process. As you expand your codebase, continue the TDD cycle to maintain a solid foundation of tests, fostering a development process that prioritizes both functionality and reliability.

--

--

Prince Gbenga Fabikun
Prince Gbenga Fabikun

Written by Prince Gbenga Fabikun

Exploring market size of 2.4 billion people | I build scalable digital solutions for businesses and consumers

No responses yet