NestJS Testing Expert
Build reliable Vitest suites for NestJS modules, services, controllers, and HTTP endpoints.
Scope
This skill owns NestJS mechanics: testing modules, provider wiring, and
request-level end-to-end flows. For framework-agnostic questions — which level a
behavior belongs at, what a coverage number means, how to design a test that
survives refactoring, how to kill a flake — use testing-expert.
When to Use
- Writing unit, integration, or end-to-end tests for a NestJS application
- Setting up a testing module, overriding providers, or faking a database
- Testing controllers, guards, interceptors, or pipes
- Exercising HTTP endpoints against a booted Nest application
Levels in a Nest Application
- Unit — a service or provider in isolation, collaborators replaced with test doubles. No module graph beyond the providers under test.
- Integration — a module compiled with its real providers, external systems faked. Proves the wiring and the contracts between layers.
- End-to-end — a booted application driven over HTTP. Proves the full request path: pipes, guards, controller, service, persistence.
Vitest Setup for NestJS
Nest's dependency injection reads decorator metadata (emitDecoratorMetadata).
Vitest transforms with esbuild, which does not emit it, so constructor
injection silently resolves to undefined. Compile with unplugin-swc, which
does emit decorator metadata.
Keep end-to-end specs in a separate config so bun run test stays fast and
bun run test:e2e boots the application:
Options from older runner configs map to Vitest one to one: path aliases move from
moduleNameMapper to resolve.alias (or the vite-tsconfig-paths plugin),
testEnvironment becomes test.environment, transform is replaced by the SWC
plugin, and per-suite timeouts use test.testTimeout or vi.setConfig({ testTimeout }).
Import describe, it, expect, vi, and the lifecycle hooks from vitest
explicitly when globals is off. Browser-driven end-to-end flows belong to
Playwright; Supertest here covers HTTP against a booted Nest app.
For auto-mocking whole providers, createMock from @golevelup/ts-vitest
returns a deep mock typed to the class:
Service Specs
Compile a testing module with the subject real and every collaborator supplied explicitly. Injection tokens come from whatever integration provides them — an ORM's token helper, a class reference, or a custom token constant.
Assert on the arguments the collaborator received when they encode a business
rule — the isDeleted: false filter above is the behavior, not an implementation
detail.
Controller Specs
Register the controller, stub its service, and verify only the controller's own job: argument extraction, delegation, and response shaping. Business rules belong to the service spec.
Overriding Providers
Compile the real module and swap only what must not run for real.
overrideProvider and overrideGuard keep the rest of the graph authentic, so
the test still proves the wiring.
Override the guard only in tests about something else. Authorization itself deserves end-to-end tests that run the real guard.
Integration Tests
Boot the application, keep persistence real against a disposable database, and reset state between tests.
Close the application in afterAll. A leaked Nest application holds its
connection pool open and hangs the Vitest run.
End-to-End Tests
Same boot, driven as a real client — authentication included.
A shared signInTestUser helper keeps the token flow in one place and out of
every spec.
Database Strategy
Reset between tests by truncating or rolling back a transaction rather than recreating the schema — schema rebuilds dominate suite runtime.
Tips
- Give each spec its own module compilation; a shared one leaks state between tests.
- Reset mocks in
afterEachso a stub set in one test cannot satisfy the next. - Boot the application once per describe block and reset data per test — booting per test is the usual cause of a slow Nest suite.
- Keep tests deterministic: freeze the clock (
vi.useFakeTimers()withvi.setSystemTime) and pin the timezone rather than asserting on the real one. - Restore spies with
vi.restoreAllMocks()inafterEachwhen a spec usesvi.spyOnon shared objects.
Checklist
- Clear arrange/act/assert structure
- Subject real, collaborators explicit — never the reverse
- Error and unauthorized paths covered, not just the happy path
- Application closed and mocks reset in teardown
- Fast to run


