Mocking & Overriding Providers
Tests should exercise the unit you care about while everything else behaves predictably. NestJS makes this possible because the same dependency-injection container that wires your app at runtime is rebuilt by Test.createTestingModule, and its fluent override API lets you swap any provider, guard, interceptor, pipe, or filter for a fake. This page covers the override methods, Jest’s auto-mocking helpers, and typed mock libraries such as @golevelup/ts-jest so your mocks stay in sync with the real interfaces they replace.
Overriding providers
When you import a real feature module to get its full wiring, you rarely want every collaborator to be real. The .overrideProvider(token) chain replaces a single provider after the module definition but before .compile(). It accepts the same shapes as a custom provider: useValue, useClass, and useFactory.
import { Test, TestingModule } from '@nestjs/testing';
import { CatsModule } from './cats.module';
import { CatsRepository } from './cats.repository';
import { MailService } from '../mail/mail.service';
describe('CatsModule (mocked deps)', () => {
let module: TestingModule;
const repoMock = {
findById: jest.fn(),
findAll: jest.fn(),
};
beforeEach(async () => {
module = await Test.createTestingModule({
imports: [CatsModule],
})
.overrideProvider(CatsRepository)
.useValue(repoMock)
.overrideProvider(MailService)
.useClass(FakeMailService) // a real class with no side effects
.compile();
});
afterEach(() => jest.clearAllMocks());
});
The override targets the injection token, so it works for class tokens and string/symbol tokens alike. If a provider appears in several imported modules, a single override replaces it everywhere it is injected.
Overriding guards, interceptors, pipes, and filters
End-to-end and controller tests often fail because a real AuthGuard rejects the request, or a RolesGuard needs a populated user. Rather than forging valid JWTs, override the guard so it always allows the request. The corresponding methods are .overrideGuard(), .overrideInterceptor(), .overridePipe(), and .overrideFilter().
import { CanActivate } from '@nestjs/common';
import { AuthGuard } from '../auth/auth.guard';
import { LoggingInterceptor } from '../common/logging.interceptor';
const allowGuard: CanActivate = { canActivate: () => true };
const module = await Test.createTestingModule({
imports: [CatsModule],
})
.overrideGuard(AuthGuard)
.useValue(allowGuard)
.overrideInterceptor(LoggingInterceptor)
.useValue({ intercept: (_ctx, next) => next.handle() })
.compile();
Tip: an override applies regardless of where the enhancer is bound — controller-level
@UseGuards(), method-level, or global viaAPP_GUARD. Overriding the class token catches all of them.
Auto-mocking with useMocker
Listing a mock for every dependency is tedious in large graphs. .useMocker() supplies a factory that Nest calls for any token it cannot otherwise resolve, letting you mock unspecified dependencies in bulk. Combine it with the MockFactory token to inspect what is being requested.
import { ModuleMocker, MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global);
const module = await Test.createTestingModule({
providers: [CatsService],
})
.useMocker((token) => {
if (typeof token === 'function') {
const meta = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(meta);
return new Mock();
}
})
.compile();
const service = module.get(CatsService);
Every method on the auto-generated mock is a jest.fn() returning undefined, so you still program the cases you assert on.
Typed mocks with @golevelup/ts-jest
Hand-written jest.fn() objects drift from the real interface and lose type safety. @golevelup/ts-jest provides createMock<T>(), which returns a deeply mocked object where every method is already a typed Jest mock — including nested objects like an ExecutionContext.
npm install --save-dev @golevelup/ts-jest
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { ExecutionContext } from '@nestjs/common';
import { CatsRepository } from './cats.repository';
const repo: DeepMocked<CatsRepository> = createMock<CatsRepository>();
repo.findById.mockResolvedValue({ id: 1, name: 'Felix', age: 3 });
// Nested mocks work out of the box:
const ctx = createMock<ExecutionContext>();
ctx.switchToHttp().getRequest.mockReturnValue({ user: { id: 7 } });
Provide the typed mock to the module the same way as any other value:
const module = await Test.createTestingModule({
providers: [
CatsService,
{ provide: CatsRepository, useValue: repo },
],
}).compile();
Output:
PASS src/cats/cats.service.spec.ts
CatsService (typed mocks)
✓ returns the cat when it exists (2 ms)
✓ short-circuits when repository is empty (1 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Time: 0.974 s
Choosing a mocking approach
| Approach | API | Best for |
|---|---|---|
| Static value | { provide, useValue } | Small, explicit stubs |
| Override on imported module | .overrideProvider().useValue() | Replacing one provider in a real module |
| Enhancer override | .overrideGuard() / .overrideInterceptor() | Bypassing auth/logging in e2e tests |
| Bulk auto-mock | .useMocker() | Large graphs with many leaf dependencies |
| Typed deep mock | createMock<T>() | Type-safe mocks, nested objects, contexts |
Warning: overrides resolve against the injection token, not the variable name. If two distinct classes share a string token, an override hits both — prefer unique class or symbol tokens to avoid surprises.
Best practices
- Override against the token your code actually injects; a mismatched token silently leaves the real provider in place.
- Prefer
createMock<T>()orjest.Mocked<T>over loose objects so interface changes surface as compile errors. - Use
.overrideGuard()to neutralize auth in tests instead of constructing valid credentials or tokens. - Reach for
.useMocker()only when explicit mocks become noise; keep critical collaborators explicit so intent stays readable. - Reset mock state between tests with
jest.clearAllMocks()orclearMocks: trueto keep cases order-independent. - Assert on mock interactions (
toHaveBeenCalledWith) so an override does not hide a broken call you meant to verify.