DEV Community

Neweraofcoding
Neweraofcoding

Posted on

Getting Started with Unit Testing in a Node.js Project with Jest

When building backend applications with Node.js, most developers focus heavily on APIs, databases, and business logic—but often skip testing until bugs start showing up.

Unit testing helps you catch bugs early, improve code quality, and refactor with confidence.

In this blog, we’ll learn how to set up unit testing in a Node.js + TypeScript project using Jest.


What is Unit Testing?

Unit testing means testing small isolated pieces of code (functions, services, controllers) to verify they work as expected.

Example:

Instead of testing the full API flow:

Client → Route → Controller → Database

We test only:

Function → Input → Expected Output

This makes debugging faster and easier.


Why Use Jest?

Jest is one of the most popular testing frameworks because it provides:

✅ Zero-config setup
✅ Fast execution
✅ Mocking support
✅ Built-in assertions
✅ TypeScript support
✅ Code coverage reports


Step 1: Create a Node.js Project

Initialize a project:

npm init -y
Enter fullscreen mode Exit fullscreen mode

Install TypeScript:

npm install typescript ts-node @types/node -D
Enter fullscreen mode Exit fullscreen mode

Initialize TypeScript config:

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Jest

Install Jest and TypeScript support:

npm install jest ts-jest @types/jest -D
Enter fullscreen mode Exit fullscreen mode

Initialize Jest config:

npx ts-jest config:init
Enter fullscreen mode Exit fullscreen mode

This creates:

jest.config.js
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure TypeScript

Update tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Important settings:

  • target → JavaScript version output
  • module → module system
  • esModuleInterop → better import compatibility

Step 4: Create Sample Code

Project structure:

src/
 ├── controllers/
 │   └── itemController.ts
 ├── models/
 │   └── item.ts
tests/
 └── itemController.test.ts
Enter fullscreen mode Exit fullscreen mode

Create model:

// src/models/item.ts

export const items: string[] = [];
Enter fullscreen mode Exit fullscreen mode

Create controller:

// src/controllers/itemController.ts

import { Request, Response } from 'express';
import { items } from '../models/item';

export const getItems = (
  req: Request,
  res: Response
) => {
  res.json(items);
};
Enter fullscreen mode Exit fullscreen mode

Step 5: Write Your First Test

Create:

// tests/itemController.test.ts

import { Request, Response } from 'express';
import { getItems } from '../src/controllers/itemController';
import { items } from '../src/models/item';

describe('Item Controller', () => {
  it('should return empty array', () => {
    const req = {} as Request;

    const res = {
      json: jest.fn()
    } as unknown as Response;

    items.length = 0;

    getItems(req, res);

    expect(res.json).toHaveBeenCalledWith([]);
  });
});
Enter fullscreen mode Exit fullscreen mode

Understanding the Test

describe()

Groups related tests.

describe('Item Controller', () => {})
Enter fullscreen mode Exit fullscreen mode

it()

Defines a single test case.

it('should return empty array', () => {})
Enter fullscreen mode Exit fullscreen mode

jest.fn()

Creates a mock function.

json: jest.fn()
Enter fullscreen mode Exit fullscreen mode

Useful for checking:

  • was it called?
  • how many times?
  • what arguments?

expect()

Assertion API.

expect(res.json).toHaveBeenCalledWith([]);
Enter fullscreen mode Exit fullscreen mode

Verifies output.


Step 6: Add Test Script

Update package.json

{
  "scripts": {
    "test": "jest"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run tests:

npm test
Enter fullscreen mode Exit fullscreen mode

Output:

PASS tests/itemController.test.ts
Enter fullscreen mode Exit fullscreen mode

Common Jest Matchers

Check equality

expect(value).toBe(10);
Enter fullscreen mode Exit fullscreen mode

Check object equality

expect(obj).toEqual({});
Enter fullscreen mode Exit fullscreen mode

Check if function was called

expect(mockFn).toHaveBeenCalled();
Enter fullscreen mode Exit fullscreen mode

Check call count

expect(mockFn).toHaveBeenCalledTimes(1);
Enter fullscreen mode Exit fullscreen mode

Mocking Dependencies

Suppose your controller calls a service:

import { getData } from './service';
Enter fullscreen mode Exit fullscreen mode

Mock it:

jest.mock('./service');
Enter fullscreen mode Exit fullscreen mode

Set return value:

(getData as jest.Mock).mockReturnValue([]);
Enter fullscreen mode Exit fullscreen mode

This isolates your unit.


Running Coverage

Generate coverage report:

npm test -- --coverage
Enter fullscreen mode Exit fullscreen mode

Output:

Statements   : 95%
Branches     : 90%
Functions    : 100%
Lines        : 96%
Enter fullscreen mode Exit fullscreen mode

Aim for good coverage, not just high numbers.


Best Practices

Keep tests isolated

Each test should run independently.


Mock external dependencies

Avoid hitting real databases or APIs.


Use descriptive test names

Good:

should return empty items array
Enter fullscreen mode Exit fullscreen mode

Bad:

test 1
Enter fullscreen mode Exit fullscreen mode

Follow AAA Pattern

Arrange → Act → Assert

Example:

// Arrange
const req = {};

// Act
getItems(req, res);

// Assert
expect(res.json).toHaveBeenCalled();
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Unit testing is not optional in production-grade applications.

With Jest, getting started is simple:

  1. Install Jest
  2. Configure TypeScript
  3. Write tests
  4. Mock dependencies
  5. Run coverage

Start small.

Test one controller.

Then one service.

Then one utility.

Over time, your project becomes safer, cleaner, and easier to maintain.

Your future self will thank you.

Top comments (0)