Simple user management example demonstrating Suites with NestJS and Jest, showcasing advanced mock configuration patterns using .mock().final() and .mock().impl().
- Node.js 18 or higher
- pnpm installed globally
- ✅ Solitary unit tests - Test UserService in complete isolation
- ✅ Sociable unit tests - Test components together with real validation, mocked I/O
- ✅
.mock().final()- Immutable mock configuration with plain functions - ✅
.mock().impl()- Flexible mock configuration with stub functions - ✅ Token injection - DATABASE_TOKEN as external boundary
- ✅ Class injection - UserValidator and UserRepository
pnpm install
pnpm testAll tests should pass, demonstrating both testing strategies with advanced mock configuration.
src/ - Application code being tested:
src/
├── types.ts # User types and interfaces
├── user.validator.ts # Validation logic (no dependencies)
├── user.repository.ts # Data access (token injection)
└── user.service.ts # Business logic (class injections)
tests/ - Tests demonstrating Suites advanced mock configuration:
tests/
├── user.solitary.spec.ts # Solitary tests with .mock().final() and .mock().impl()
└── user.sociable.spec.ts # Sociable tests with .mock().final() and .mock().impl()
Use when you want to lock down mock behavior that should never change:
const { unit, unitRef } = await TestBed.solitary(UserService)
.mock(UserValidator)
.final({
// Plain functions - cannot be reconfigured in tests
validate: () => ({ isValid: true, errors: [] })
})
.compile();Key characteristics:
- Functions provided to
.final()are plain functions, not Jest mocks - Behavior is locked - tests cannot use
mockReturnValue()or similar - Call inspection (
toHaveBeenCalled()) is not available - Best for: external services, logging, fixed configuration values
Use when you want sensible defaults that tests can override:
const { unit, unitRef } = await TestBed.solitary(UserService)
.mock(UserValidator)
.impl((stubFn) => ({
// Stubs - can be reconfigured and inspected in tests
validate: stubFn().mockReturnValue({ isValid: true, errors: [] })
}))
.compile();
// Later in tests, you can override:
validator.validate.mockReturnValue({ isValid: false, errors: ['Error'] });Key characteristics:
- Uses
stubFn()factory to create Jest mock functions - Behavior is flexible - tests can reconfigure using
mockReturnValue(), etc. - Call inspection (
toHaveBeenCalled(),toHaveBeenCalledWith()) is available - Best for: most mocks where different tests need different behaviors
When to use .final():
- External APIs (email, SMS, payments) - prevent accidental real calls
- Configuration/settings - fixed test environment values
- Logging/telemetry - consistent, predictable output
When to use .impl():
- Database operations - need to simulate different query results
- Collaborator services - need flexibility for different test scenarios
- Any mock where behavior needs to vary per-test
| Feature | .final() |
.impl() |
|---|---|---|
| Reconfigurable | ❌ No | ✅ Yes |
| Call inspection | ❌ No | ✅ Yes |
| Function type | Plain functions | Jest stubs |
mockReturnValue() |
❌ Cannot use | ✅ Can use |
toHaveBeenCalled() |
❌ Cannot use | ✅ Can use |
- nestjs-jest - Basic NestJS + Jest example
- nestjs-vitest - NestJS with Vitest
- inversify-jest - InversifyJS with Jest