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.
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.
You now know how to test controllers. Next, explore Lifecycle Events.