NestJS Unit Testing
Unit testing verifies that one isolated piece of code — a service method, a guard, or a pipe — behaves correctly for a given input. Each test focuses on a single unit and replaces all its dependencies with fakes (mocks). Unit tests run in milliseconds because they touch no database, no network, and no file system.
Testing Tools in NestJS
Tool | Role --------------|------------------------------------------ Jest | Test runner, assertions, mocking (pre-installed) @nestjs/testing | Creates a testing module with DI support ts-jest | Compiles TypeScript for Jest (pre-configured)
The .spec.ts File Convention
Every generated service and controller has a companion .spec.ts file. The CLI creates them automatically:
src/users/ users.service.ts users.service.spec.ts ← unit tests for UsersService users.controller.ts users.controller.spec.ts ← unit tests for UsersController
Testing a Service
// users.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from './user.entity';
describe('UsersService', () => {
let service: UsersService;
const mockUserRepository = {
find: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: mockUserRepository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('findAll', () => {
it('returns an array of users', async () => {
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
mockUserRepository.find.mockResolvedValue(users);
const result = await service.findAll();
expect(result).toEqual(users);
expect(mockUserRepository.find).toHaveBeenCalledTimes(1);
});
});
describe('findOne', () => {
it('returns the user when found', async () => {
const user = { id: 1, name: 'Alice' };
mockUserRepository.findOne.mockResolvedValue(user);
const result = await service.findOne(1);
expect(result).toEqual(user);
});
it('throws NotFoundException when user is not found', async () => {
mockUserRepository.findOne.mockResolvedValue(null);
await expect(service.findOne(999)).rejects.toThrow('User #999 not found');
});
});
});
Testing a Controller
// users.controller.spec.ts
describe('UsersController', () => {
let controller: UsersController;
const mockUsersService = {
findAll: jest.fn().mockResolvedValue([{ id: 1, name: 'Alice' }]),
findOne: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
create: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
};
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [UsersController],
providers: [{ provide: UsersService, useValue: mockUsersService }],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('findAll returns array of users', async () => {
const result = await controller.findAll();
expect(result).toHaveLength(1);
expect(mockUsersService.findAll).toHaveBeenCalled();
});
it('findOne calls service with correct id', async () => {
await controller.findOne('1');
expect(mockUsersService.findOne).toHaveBeenCalledWith(1);
});
});
Jest Assertion Reference
expect(value).toBe(exact) // strict equality (===) expect(value).toEqual(obj) // deep equality (object contents) expect(value).toBeDefined() // not undefined expect(value).toBeNull() // is null expect(value).toBeTruthy() // any truthy value expect(value).toHaveLength(n) // array/string length expect(fn).toHaveBeenCalled() // mock was called expect(fn).toHaveBeenCalledWith(a) // mock was called with argument expect(fn).toHaveBeenCalledTimes(n)// mock called exactly n times expect(promise).rejects.toThrow() // async function throws
Running Tests
npm run test ← run all unit tests once npm run test:watch ← re-run tests on file save npm run test:cov ← run with coverage report
Unit Test Diagram
Real Application: UsersController → UsersService → UserRepository → PostgreSQL Unit Test (service): UsersService → mockUserRepository (fake, returns test data) No database. No HTTP. Runs in milliseconds. Unit Test (controller): UsersController → mockUsersService (fake, returns test data) No service logic executed. Tests only controller behavior.
Unit tests give you confidence that each piece of your application works correctly in isolation. When a test fails after you make a change, you know immediately which unit broke and why — without manually testing the entire application. A service with 10 well-written unit tests is easier to refactor safely than one with no tests at all.
