Skip to content

Testing NestJS Controllers

Controller tests verify that HTTP inputs are delegated correctly to services and that results/exceptions map as expected. Controllers should stay thin, so these tests stay small.

Controller Unit Test Pattern

Provide the controller and a mocked service in TestingModule. Call controller methods directly with DTO stubs (unit style) or use e2e for decorator/pipe integration.

const service = { findAll: jest.fn().mockResolvedValue([{ id: 1 }]) };
const moduleRef = await Test.createTestingModule({
  controllers: [CatsController],
  providers: [{ provide: CatsService, useValue: service }],
}).compile();
const controller = moduleRef.get(CatsController);
await expect(controller.findAll()).resolves.toEqual([{ id: 1 }]);

Direct controller method calls bypass pipes/guards; use e2e tests when you need those layers.

What Belongs in Controller Tests

YES: delegation to service, DTO mapping, status-oriented method behavior
NO: business rules (test in services), ValidationPipe quirks (test e2e)
  • If a controller method only returns this.service.x(), a single delegation test may be enough.
  • Test exception propagation when controllers rethrow or translate errors.
  • Prefer e2e for @Param pipes like ParseIntPipe and auth guards.
  • Keep controllers thin to avoid complex controller tests.

Controller Testing Cheatsheet

Choose the right layer for each assertion.

Concern Best Layer
Service called correctly Controller unit
Business rule Service unit
DTO validation E2E / pipe test
Auth guard blocking E2E / guard unit
Serialization interceptor E2E

Guards in Unit Controller Tests

Guards do not run when you call controller methods directly. Override or e2e-test guards separately rather than assuming unit controller tests cover security.

Thin Controllers Pay Off

The thinner the controller, the less you need elaborate controller tests—most confidence comes from service and e2e layers.

Common Mistakes

  • Expecting ValidationPipe to run in direct controller unit calls.
  • Re-implementing service logic assertions in controller tests.
  • Skipping e2e coverage for authenticated routes entirely.
  • Over-mocking until tests mirror implementation line-by-line.

Key Takeaways

  • Controller unit tests check delegation and simple mapping.
  • Pipes and guards need e2e or dedicated tests.
  • Thin controllers keep this layer easy to verify.
  • Pick the cheapest test layer that can catch the bug class.

Pro Tip

If controller unit tests grow large, treat that as a smell—move logic into services and keep controllers as HTTP adapters.