bundles/backend/skills/nestjs-testing-expert/SKILL.md
NestJS testing mechanics with Jest — building testing modules, mocking providers and repositories, writing service and controller specs, and driving HTTP end-to-end tests through the real application. Use for any test touching a NestJS service, controller, guard, module, or API endpoint, including test-module setup, provider overrides, database fakes, and Supertest request flows.
npx skillsauth add shipshitdev/library nestjs-testing-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build reliable Jest suites for NestJS modules, services, controllers, and HTTP endpoints.
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.
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.
describe('UsersService', () => {
let service: UsersService;
let repository: jest.Mocked<UsersRepository>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{ provide: UsersRepository, useValue: createUsersRepositoryMock() },
],
}).compile();
service = module.get(UsersService);
repository = module.get(UsersRepository);
});
afterEach(() => {
jest.resetAllMocks();
});
it('returns only the active users of the requested organization', async () => {
const expected = [{ id: '1', organization: 'org1' }];
repository.find.mockResolvedValue(expected);
const result = await service.findAll('org1');
expect(result).toEqual(expected);
expect(repository.find).toHaveBeenCalledWith({
organization: 'org1',
isDeleted: false,
});
});
});
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.
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.
describe('UsersController', () => {
let controller: UsersController;
let service: jest.Mocked<UsersService>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [{ provide: UsersService, useValue: createUsersServiceMock() }],
}).compile();
controller = module.get(UsersController);
service = module.get(UsersService);
});
it('passes the organization from the request through to the service', async () => {
const expected = [{ id: '1', email: '[email protected]' }];
service.findAll.mockResolvedValue(expected);
const result = await controller.findAll('org1');
expect(result).toEqual(expected);
expect(service.findAll).toHaveBeenCalledWith('org1');
});
});
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.
const module: TestingModule = await Test.createTestingModule({
imports: [UsersModule],
})
.overrideProvider(MailerService)
.useValue(mailerMock)
.overrideGuard(AuthGuard)
.useValue({ canActivate: () => true })
.compile();
Override the guard only in tests about something else. Authorization itself deserves end-to-end tests that run the real guard.
Boot the application, keep persistence real against a disposable database, and reset state between tests.
describe('Users (integration)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
beforeEach(async () => {
await resetDatabase();
});
it('persists a created user and returns it on read', async () => {
const created = await request(app.getHttpServer())
.post('/api/users')
.send({ email: '[email protected]', name: 'Test' })
.expect(201);
const read = await request(app.getHttpServer())
.get(`/api/users/${created.body.id}`)
.expect(200);
expect(read.body.email).toBe('[email protected]');
});
});
Close the application in afterAll. A leaked Nest application holds its
connection pool open and hangs the Jest run.
Same boot, driven as a real client — authentication included.
describe('Users API (e2e)', () => {
let app: INestApplication;
let authToken: string;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
authToken = await signInTestUser(app);
});
afterAll(async () => {
await app.close();
});
it('rejects an unauthenticated list request', () =>
request(app.getHttpServer()).get('/api/users').expect(401));
it('returns the caller organization users when authenticated', () =>
request(app.getHttpServer())
.get('/api/users')
.set('Authorization', `Bearer ${authToken}`)
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
}));
});
A shared signInTestUser helper keeps the token flow in one place and out of
every spec.
| Approach | Fits | Cost | |---|---|---| | Repository test double | Unit specs | Fastest; proves no SQL or schema | | In-memory or embedded engine | Integration specs | Fast; behavior can drift from production | | Disposable container per run | Integration and e2e specs | Slowest; highest fidelity |
Reset between tests by truncating or rolling back a transaction rather than recreating the schema — schema rebuilds dominate suite runtime.
afterEach so a stub set in one test cannot satisfy the next.development
Coordinates a weekly engineering review of board accuracy, recent code changes, operational health, and scoped cleanup. Use for a recurring repository health review or a review of the last several days.
testing
Audits project board configuration and prepares explicitly requested setup, copy, or normalization changes while preserving the existing workflow and provider boundaries. Use when inspecting a board's fields, columns, scope, or configuration.
testing
Reconciles a project board with current work and delivery evidence, reports incomplete coverage and metadata gaps, and applies only approved provider-supported field changes. Use when auditing board drift, reviewing blocked work, or assessing upcoming delivery.
development
Walk through how a subsystem works. Use for "how does X work", code walkthroughs before changing something, and placement or ownership questions. Explains architecture, runtime flow, and onboarding mental models. Can critique architecture. Use why for motivation.