Unit testing controllers helps check if each part of your app that handles user requests works correctly on its own. It finds mistakes early and keeps your app reliable.
Unit testing controllers in NestJS
describe('ControllerName', () => { let controller: ControllerName; let service: ServiceName; beforeEach(async () => { const module = await Test.createTestingModule({ controllers: [ControllerName], providers: [ { provide: ServiceName, useValue: { methodName: jest.fn() }, }, ], }).compile(); controller = module.get<ControllerName>(ControllerName); service = module.get<ServiceName>(ServiceName); }); it('should do something', () => { // test code here }); });
Use Test.createTestingModule to set up a test environment for your controller.
Mock services with useValue to isolate controller tests from real service logic.
findAll method in a CatsController. The service is mocked to return a fixed list.describe('CatsController', () => { let controller: CatsController; let service: CatsService; beforeEach(async () => { const module = await Test.createTestingModule({ controllers: [CatsController], providers: [ { provide: CatsService, useValue: { findAll: jest.fn().mockReturnValue(['cat1', 'cat2']) }, }, ], }).compile(); controller = module.get<CatsController>(CatsController); service = module.get<CatsService>(CatsService); }); it('should return an array of cats', () => { expect(controller.findAll()).toEqual(['cat1', 'cat2']); }); });
create method that adds a user. The service mock returns the user with an ID.describe('UsersController', () => { let controller: UsersController; let service: UsersService; beforeEach(async () => { const module = await Test.createTestingModule({ controllers: [UsersController], providers: [ { provide: UsersService, useValue: { createUser: jest.fn().mockImplementation(user => ({ id: 1, ...user })) }, }, ], }).compile(); controller = module.get<UsersController>(UsersController); service = module.get<UsersService>(UsersService); }); it('should create a user', () => { const userDto = { name: 'Alice' }; expect(controller.create(userDto)).toEqual({ id: 1, name: 'Alice' }); }); });
This test checks two controller methods: findAll returns a list of books, and findOne returns a single book by id. The service is mocked to return fixed values.
import { Test } from '@nestjs/testing'; import { BooksController } from './books.controller'; import { BooksService } from './books.service'; describe('BooksController', () => { let controller: BooksController; let service: BooksService; beforeEach(async () => { const module = await Test.createTestingModule({ controllers: [BooksController], providers: [ { provide: BooksService, useValue: { findAll: jest.fn().mockReturnValue(['Book A', 'Book B']), findOne: jest.fn().mockImplementation(id => `Book ${id}`), }, }, ], }).compile(); controller = module.get<BooksController>(BooksController); service = module.get<BooksService>(BooksService); }); it('should return all books', () => { expect(controller.findAll()).toEqual(['Book A', 'Book B']); }); it('should return one book by id', () => { expect(controller.findOne('A')).toBe('Book A'); }); });
Always mock services to keep controller tests focused and fast.
Use jest.fn() to create mock functions and control their return values.
Run tests often to catch errors early and keep your code healthy.
Unit testing controllers checks if they handle requests correctly by themselves.
Mock services to isolate controller logic from other parts.
Use NestJS testing tools like Test.createTestingModule and Jest mocks.
Practice
Solution
Step 1: Understand the role of a controller
Controllers handle incoming requests and return responses, so testing them focuses on this behavior.Step 2: Identify what unit testing isolates
Unit testing isolates the controller from other parts like services or databases to test its logic alone.Final Answer:
To verify the controller handles requests correctly without depending on other parts -> Option AQuick Check:
Unit testing controller logic = To verify the controller handles requests correctly without depending on other parts [OK]
- Confusing unit tests with integration tests
- Testing database or UI in controller unit tests
- Ignoring mocking dependencies
Solution
Step 1: Recall NestJS testing syntax
Use Test.createTestingModule with a config object specifying controllers or providers, then compile it.Step 2: Identify correct property for controllers
The property to register controllers is 'controllers', not 'services' or 'providers' for controllers.Final Answer:
const module = await Test.createTestingModule({ controllers: [MyController] }).compile(); -> Option CQuick Check:
Use controllers array in createTestingModule = const module = await Test.createTestingModule({ controllers: [MyController] }).compile(); [OK]
- Using 'services' instead of 'controllers' key
- Forgetting to await compile()
- Using new Module() instead of Test.createTestingModule
result be?class MyController {
getHello() {
return 'Hello World!';
}
}
// Test snippet
const controller = new MyController();
const result = controller.getHello();Solution
Step 1: Analyze the controller method
The method getHello returns the string 'Hello World!' synchronously.Step 2: Check the test call
Calling getHello() directly returns the string without errors or async behavior.Final Answer:
'Hello World!' -> Option DQuick Check:
Direct method call returns string = 'Hello World!' [OK]
- Assuming async behavior without async keyword
- Expecting undefined if not mocked
- Confusing method call with property access
describe('MyController', () => {
let controller: MyController;
beforeEach(() => {
controller = new MyController();
});
it('should return greeting', () => {
expect(controller.getHello()).toBe('Hello World!');
});
});Solution
Step 1: Check controller instantiation
Creating controller directly is allowed but only if it has no dependencies.Step 2: Identify missing mocking
If the controller depends on a service, it must be mocked to isolate controller logic.Final Answer:
The test does not mock the service dependency -> Option BQuick Check:
Missing mocks for dependencies = The test does not mock the service dependency [OK]
- Assuming direct instantiation is always wrong
- Forgetting to mock injected services
- Confusing toBe and toEqual for strings
Solution
Step 1: Understand async service method
The service method returns a promise, so the mock must simulate a resolved promise.Step 2: Use jest.fn().mockResolvedValue
This creates a mock that returns a resolved promise with the given value, allowing the controller method to await it.Step 3: Await controller method in test
The test must await the controller method to handle the async call properly.Final Answer:
Mock the service method with jest.fn().mockResolvedValue(value) and await the controller method in the test -> Option AQuick Check:
Use mockResolvedValue and await async calls = Mock the service method with jest.fn().mockResolvedValue(value) and await the controller method in the test [OK]
- Using mockReturnValue for async methods
- Not awaiting async controller methods
- Testing real service instead of mocking
Ready to prove it?
Pass the test and this pattern moves unknown
