| name | nestjs-testing |
| user-invocable | false |
| description | Use when nestJS testing with unit tests, integration tests, and e2e tests. Use when building well-tested NestJS applications. |
| allowed-tools | ["Bash","Read"] |
NestJS Testing
Master testing in NestJS for building reliable applications with
comprehensive unit, integration, and end-to-end tests.
Unit Testing Setup
Creating and configuring test modules with TestingModule.
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
describe('UserService', () => {
let service: UserService;
let module: TestingModule;
beforeEach(async () => {
module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: {
find: jest.fn(),
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn(),
delete: jest.fn(),
},
},
],
}).compile();
service = module.get<UserService>(UserService);
});
afterEach(async () => {
await module.close();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should find all users', async () => {
const users = [{ id: 1, name: 'John' }];
jest.spyOn(service, 'findAll').mockResolvedValue(users);
const result = await service.findAll();
expect(result).toEqual(users);
expect(service.findAll).toHaveBeenCalled();
});
});
describe('ConfigService', () => {
let service: ConfigService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
{
provide: ConfigService,
useFactory: () => {
return new ConfigService('.env.test');
},
},
],
}).compile();
service = module.get<ConfigService>(ConfigService);
});
it('should load config from test environment', () => {
expect(service.get('NODE_ENV')).toBe('test');
});
});
Testing Controllers
Mocking services and testing request/response handling.
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { NotFoundException } from '@nestjs/common';
describe('UserController', () => {
let controller: UserController;
let service: UserService;
const mockUserService = {
findAll: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserController],
: [
{
: ,
: mockUserService,
},
],
}).();
controller = .<>();
service = .<>();
});
( {
jest.();
});
(, {
(, () => {
users = [
{ : , : , : },
{ : , : , : },
];
mockUserService..(users);
result = controller.();
(result).(users);
(service.).();
});
(, () => {
mockUserService..([]);
result = controller.();
(result).([]);
});
});
(, {
(, () => {
user = { : , : , : };
mockUserService..(user);
result = controller.();
(result).(user);
(service.).();
});
(, () => {
mockUserService..(
(),
);
(controller.())..(
,
);
});
});
(, {
(, () => {
: = {
: ,
: ,
: ,
};
createdUser = { : , ...createUserDto };
mockUserService..(createdUser);
result = controller.(createUserDto);
(result).(createdUser);
(service.).(createUserDto);
});
});
(, {
(, () => {
updateDto = { : };
updatedUser = { : , : , : };
mockUserService..(updatedUser);
result = controller.(, updateDto);
(result).(updatedUser);
(service.).(, updateDto);
});
});
(, {
(, () => {
mockUserService..({ : });
result = controller.();
(result).({ : });
(service.).();
});
});
});
Testing Services
Mocking repositories and database operations.
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { getRepositoryToken } from '@nestjs/typeorm';
import { NotFoundException, ConflictException } from '@nestjs/common';
describe('UserService', () => {
let service: UserService;
let repository: Repository<User>;
const mockRepository = {
find: jest.fn(),
findOne: jest.fn(),
findOneBy: jest.fn(),
save: jest.fn(),
create: jest.fn(),
delete: jest.fn(),
update: jest.fn(),
};
beforeEach( () => {
: = .({
: [
,
{
: (),
: mockRepository,
},
],
}).();
service = .<>();
repository = .<<>>(());
});
(, {
(, () => {
users = [{ : , : , : }];
mockRepository..(users);
result = service.();
(result).(users);
(repository.).();
});
});
(, {
(, () => {
user = { : , : , : };
mockRepository..(user);
result = service.();
(result).(user);
(repository.).({ : });
});
(, () => {
mockRepository..();
(service.())..();
});
});
(, {
(, () => {
createDto = {
: ,
: ,
: ,
};
user = { : , ...createDto };
mockRepository..();
mockRepository..(user);
mockRepository..(user);
result = service.(createDto);
(result).(user);
(repository.).(createDto);
(repository.).(user);
});
(, () => {
createDto = {
: ,
: ,
: ,
};
mockRepository..({ : });
(service.(createDto))..(
,
);
});
});
(, {
(, () => {
updateDto = { : };
existingUser = { : , : , : };
updatedUser = { ...existingUser, ...updateDto };
mockRepository..(existingUser);
mockRepository..(updatedUser);
result = service.(, updateDto);
(result).(updatedUser);
(repository.).();
});
});
(, {
(, () => {
user = { : , : , : };
mockRepository..(user);
mockRepository..({ : });
service.();
(repository.).();
});
(, () => {
mockRepository..();
(service.())..();
});
});
});
Testing Providers
Factory providers and async providers.
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { DatabaseService } from './database.service';
describe('Factory Providers', () => {
let databaseService: DatabaseService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: 'DATABASE_CONNECTION',
useFactory: (config: ConfigService) => {
return {
host: config.get('DB_HOST'),
port: config.get('DB_PORT'),
database: config.get('DB_NAME'),
};
},
inject: [ConfigService],
},
{
provide: ConfigService,
: {
: jest.( {
config = {
: ,
: ,
: ,
};
config[key];
}),
},
},
,
],
}).();
databaseService = .<>();
});
(, {
connection = databaseService.();
(connection.).();
(connection.).();
(connection.).();
});
});
(, {
: ;
( () => {
: = .({
: [
{
: ,
: () => {
( (resolve, ));
{ : };
},
},
],
}).();
service = .();
});
(, {
(service.).();
});
});
Testing Guards
Authentication and authorization guards.
import { Test, TestingModule } from '@nestjs/testing';
import { JwtAuthGuard } from './jwt-auth.guard';
import { JwtService } from '@nestjs/jwt';
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
describe('JwtAuthGuard', () => {
let guard: JwtAuthGuard;
let jwtService: JwtService;
const mockJwtService = {
verifyAsync: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
JwtAuthGuard,
{
provide: JwtService,
useValue: mockJwtService,
},
],
}).compile();
guard = module.get<JwtAuthGuard>(JwtAuthGuard);
jwtService = module.<>();
});
(, () => {
mockContext = ({
: { : },
});
mockJwtService..({
: ,
: ,
});
result = guard.(mockContext);
(result).();
(jwtService.).(, {
: expect.(),
});
});
(, () => {
mockContext = ({
: {},
});
(guard.(mockContext))..(
,
);
});
(, () => {
mockContext = ({
: { : },
});
mockJwtService..( ());
(guard.(mockContext))..(
,
);
});
});
(): {
{
: ({
: request,
: ({}),
}),
: ({}),
: ({}),
} ;
}
{ } ;
{ } ;
{ } ;
(, {
: ;
: ;
( {
reflector = ();
guard = (reflector);
});
(, {
jest.(reflector, ).([]);
mockContext = ({
: { : , : [] },
});
result = guard.(mockContext);
(result).();
});
(, {
jest.(reflector, ).([]);
mockContext = ({
: { : , : [] },
});
( guard.(mockContext)).();
});
(, {
jest.(reflector, ).();
mockContext = ({
: { : , : [] },
});
result = guard.(mockContext);
(result).();
});
});
Testing Interceptors
Transformation and logging interceptors.
import { Test, TestingModule } from '@nestjs/testing';
import { TransformInterceptor } from './transform.interceptor';
import { ExecutionContext, CallHandler } from '@nestjs/common';
import { of } from 'rxjs';
describe('TransformInterceptor', () => {
let interceptor: TransformInterceptor;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TransformInterceptor],
}).compile();
interceptor = module.get<TransformInterceptor>(TransformInterceptor);
});
it('should transform response data', (done) => {
const mockContext = {
switchToHttp: () => ({
getRequest: () => ({ url: '/test' }),
}),
} ;
: = {
: ({ : , : }),
};
interceptor.(mockContext, mockCallHandler).({
: {
(result).();
(result.).({ : , : });
(result).();
(result).();
(result.).();
();
},
});
});
});
{ } ;
{ } ;
(, {
: ;
: ;
mockCacheManager = {
: jest.(),
: jest.(),
};
( () => {
: = .({
: [
,
{
: ,
: mockCacheManager,
},
],
}).();
interceptor = .<>();
cacheManager = .();
});
(, (done) => {
cachedData = { : };
mockCacheManager..(cachedData);
mockContext = {
: ({
: ({ : , : }),
}),
} ;
: = {
: ({ : }),
};
result$ = interceptor.(mockContext, mockCallHandler);
result$.({
: {
(result).(cachedData);
(cacheManager.).();
();
},
});
});
(, {
freshData = { : };
mockCacheManager..();
mockContext = {
: ({
: ({ : , : }),
}),
} ;
: = {
: (freshData),
};
interceptor.(mockContext, mockCallHandler).( {
result$.({
: (result) => {
(result).(freshData);
( (resolve, ));
(cacheManager.).();
();
},
});
});
});
});
Testing Pipes
Validation and transformation pipes.
import { Test, TestingModule } from '@nestjs/testing';
import { ValidationPipe, BadRequestException } from '@nestjs/common';
import { ParseIntPipe } from '@nestjs/common';
import { ArgumentMetadata } from '@nestjs/common';
describe('ParseIntPipe', () => {
let pipe: ParseIntPipe;
beforeEach(() => {
pipe = new ParseIntPipe();
});
it('should parse valid number string', async () => {
const metadata: ArgumentMetadata = {
type: 'param',
metatype: Number,
data: 'id',
};
const result = await pipe.transform('123', metadata);
expect(result).toBe(123);
});
it('should throw error for invalid number string', async () => {
: = {
: ,
: ,
: ,
};
(pipe.(, metadata))..(
,
);
});
});
{ } ;
{ , , } ;
{
()
()
: ;
()
: ;
}
(, {
: ;
( {
pipe = ();
});
(, () => {
dto = {
: ,
: ,
};
: = {
: ,
: ,
};
result = pipe.(dto, metadata);
(result).(dto);
});
(, () => {
dto = {
: ,
: ,
};
: = {
: ,
: ,
};
(pipe.(dto, metadata))..(
,
);
});
});
Integration Testing / E2E Tests
Testing with supertest and real HTTP requests.
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from '../src/users/entities/user.entity';
describe('UserController (e2e)', () => {
let app: INestApplication;
let userRepository: any;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
userRepository = moduleFixture.(());
app.();
});
( () => {
app.();
});
( () => {
userRepository.();
});
(, {
(, {
(app.())
.()
.({
: ,
: ,
: ,
})
.()
.( {
(res.).();
(res..).();
(res..).();
(res.)..();
});
});
(, {
(app.())
.()
.({
: ,
: ,
})
.();
});
});
(, {
(, () => {
userRepository.([
{ : , : },
{ : , : },
]);
(app.())
.()
.()
.( {
(res.).();
(res.[]).();
(res.[]).();
});
});
(, {
(app.())
.()
.()
.([]);
});
});
(, {
(, () => {
user = userRepository.({
: ,
: ,
});
(app.())
.()
.()
.( {
(res..).(user.);
(res..).();
});
});
(, {
(app.()).().();
});
});
(, {
(, () => {
user = userRepository.({
: ,
: ,
});
(app.())
.()
.({ : })
.()
.( {
(res..).();
});
});
});
(, {
(, () => {
user = userRepository.({
: ,
: ,
});
(app.())
.()
.();
deletedUser = userRepository.({ : { : user. } });
(deletedUser).();
});
});
});
Testing with Database
In-memory, Docker, and test containers.
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
describe('UserService with In-Memory DB', () => {
let module: TestingModule;
let service: UserService;
beforeAll(async () => {
module = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
entities: [User],
synchronize: true,
dropSchema: true,
}),
TypeOrmModule.forFeature([User]),
],
providers: [UserService],
}).compile();
service = module.<>();
});
( () => {
.();
});
(, () => {
user = service.({
: ,
: ,
: ,
});
(user.).();
foundUser = service.(user.);
(foundUser.).();
});
});
{ , } ;
(, {
: ;
: ;
( () => {
container = ()
.({
: ,
: ,
: ,
})
.()
.();
port = container.();
= .({
: [
.({
: ,
: ,
port,
: ,
: ,
: ,
: [],
: ,
}),
.([]),
],
: [],
}).();
}, );
( () => {
.();
container.();
});
(, () => {
service = .<>();
user = service.({
: ,
: ,
: ,
});
(user.).();
});
});
Testing WebSockets
WebSocket gateway testing.
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { io, Socket } from 'socket.io-client';
import { ChatGateway } from './chat.gateway';
describe('ChatGateway (e2e)', () => {
let app: INestApplication;
let clientSocket: Socket;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
providers: [ChatGateway],
}).compile();
app = moduleFixture.createNestApplication();
await app.listen(3001);
});
afterAll(async () => {
await app.close();
});
beforeEach((done) => {
clientSocket = io('http://localhost:3001');
clientSocket.(, done);
});
( {
clientSocket.();
});
(, {
clientSocket.(, { : });
clientSocket.(, {
(data.).();
();
});
});
(, {
client2 = ();
client2.(, {
clientSocket.(, { : });
client2.(, {
(data.).();
client2.();
();
});
});
});
});
Testing GraphQL Resolvers
GraphQL testing with supertest.
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
describe('UserResolver (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
}),
UserModule,
],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
( () => {
app.();
});
(, {
(app.())
.()
.({
: ,
})
.()
.( {
(res...).();
(.(res...)).();
});
});
(, {
(app.())
.()
.({
: ,
})
.()
.( {
(res...).();
(res....).();
});
});
});
Mocking Strategies
jest.mock and custom providers.
jest.mock('./user.service');
import { UserService } from './user.service';
describe('UserController with mocked service', () => {
let controller: UserController;
beforeEach(() => {
controller = new UserController(new UserService());
});
it('should use mocked service', async () => {
jest.spyOn(UserService.prototype, 'findAll').mockResolvedValue([]);
const result = await controller.findAll();
expect(result).toEqual([]);
});
});
const mockUserService = {
findAll: jest.fn(),
findOne: jest.fn(),
} as unknown as UserService;
import axios from 'axios';
jest.mock('axios');
mockedAxios = axios jest.< axios>;
(, {
(, () => {
mockedAxios..({ : { : } });
service = ();
result = service.();
(result).({ : });
(mockedAxios.).();
});
});
() {
{
: jest.(),
: jest.(),
: jest.(),
: jest.( dto),
: jest.(),
};
}
Test Fixtures and Factories
Creating reusable test data.
export class UserFactory {
static create(overrides?: Partial<User>): User {
return {
id: 1,
name: 'John Doe',
email: 'john@example.com',
password: 'hashed_password',
createdAt: new Date(),
updatedAt: new Date(),
...overrides,
};
}
static createMany(count: number, overrides?: Partial<User>): User[] {
return Array.from({ length: count }, (_, i) =>
this.create({ id: i + 1, ...overrides }),
);
}
}
describe('UserService', () => {
it('should find users', async () => {
const users = UserFactory.();
mockRepository..(users);
result = service.();
(result).();
});
});
{
: <> = {};
(: ): {
.. = name;
;
}
(: ): {
.. = email;
;
}
(): {
.. = ;
;
}
(): {
{
: ,
: ,
: ,
: ,
: ,
: (),
: (),
....,
} ;
}
}
adminUser = ()
.()
.()
.()
.();
Code Coverage and CI/CD
Testing configuration for coverage and automation.
module.exports = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: [
'**/*.(t|j)s',
'!**/*.module.ts',
'!**/node_modules/**',
'!**/dist/**',
],
coverageDirectory: '../coverage',
testEnvironment: 'node',
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
}
}
name: Tests
user-:
: [push, pull_request]
:
:
runs-: ubuntu-latest
:
- : actions/checkout
- : actions/setup-node
:
node-:
- : npm ci
- : npm run :cov
- : coverage
: codecov/codecov-action
:
: ./coverage/lcov.
When to Use This Skill
Use nestjs-testing when:
- Building production applications that require reliability
- Implementing new features that need verification
- Refactoring code safely with confidence
- Debugging complex issues through isolated tests
- Ensuring API contracts are maintained
- Validating business logic correctness
- Setting up CI/CD pipelines
- Documenting expected behavior through tests
- Preventing regressions in existing functionality
- Meeting code quality standards and coverage requirements
NestJS Testing Best Practices
- Test isolation - Each test should be independent and not rely on others
- AAA pattern - Structure tests as Arrange, Act, Assert
- Mock external dependencies - Mock databases, APIs, and third-party services
- Use factories - Create test data with factories for consistency
- Test behavior, not implementation - Focus on what the code does, not how
- Meaningful test names - Describe what is being tested and expected outcome
- Setup and teardown - Clean up resources after tests
- Coverage goals - Aim for 80%+ coverage but focus on critical paths
- E2E for critical flows - Test important user journeys end-to-end
- Run tests in CI/CD - Automate testing in your deployment pipeline
NestJS Testing Common Pitfalls
- Testing implementation details - Tests break when refactoring
- Shared state - Tests fail when run in different orders
- Not cleaning up - Database pollution between tests
- Over-mocking - Mocking everything reduces test value
- Flaky tests - Tests that randomly fail due to timing issues
- Slow tests - Not using in-memory databases for unit tests
- Missing edge cases - Only testing happy paths
- Incomplete mocks - Missing methods on mocked services
- Not testing errors - Only testing successful scenarios
- Poor test organization - Hard to find and maintain tests
Resources