This episode covers testing in NestJS: unit testing with Jest, integration testing using @nestjs/testing, E2E testing and test database setup, plus code coverage, linting, and static analysis to maintain code quality.

An application without tests is a ticking time bomb. NestJS is designed with testing as a priority — dependency injection makes replacing dependencies with mocks very easy. Episode 17 covers unit tests, integration tests, and E2E tests to build the habit of writing tests that maintain code quality.
The Nest CLI already configures Jest when creating a project. To run tests:
npm run testJest finds files ending with .spec.ts and runs them — invoke it with npm run test.
A unit test focuses on a single unit — for example a service, with dependencies mocked:
import { Test } from "@nestjs/testing";
import { UsersService } from "./users.service";
describe("UsersService", () => {
let service: UsersService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = moduleRef.get(UsersService);
});
it("harus mengembalikan daftar user", () => {
const result = service.findAll();
expect(result).toContain("Arman");
});
});Test.createTestingModule creates a testing module that mirrors the runtime. it and expect are built-in Jest APIs.
When a service depends on a repository, replace the real dependency with useValue:
const mockRepo = {
find: jest.fn().mockResolvedValue([{ id: 1, name: "Arman" }]),
};
const moduleRef = await Test.createTestingModule({
providers: [UsersService, { provide: "UserRepository", useValue: mockRepo }],
}).compile();With useValue, real dependencies are replaced with mocks — tests become fast and isolated from the database.
Integration tests combine several real components — controllers, services, and modules — while still mocking external dependencies like the database.
describe("UsersController", () => {
let controller: UsersController;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
controllers: [UsersController],
providers: [
UsersService,
{ provide: "UserRepository", useValue: mockRepo },
],
}).compile();
controller = moduleRef.get(UsersController);
});
it("harus mengembalikan user", async () => {
const result = await controller.findAll();
expect(result).toHaveLength(1);
});
});NestJS provides .overrideProvider().useValue() to replace a provider during testing — useful when testing a full module without running heavy dependencies.
E2E tests bootstrap the entire application and send real HTTP requests using supertest:
import * as request from "supertest";
import { Test } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import { AppModule } from "../src/app.module";
describe("App (e2e)", () => {
let app: INestApplication;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it("GET / mengembalikan Hello World", () => {
return request(app.getHttpServer())
.get("/")
.expect(200)
.expect("Hello World!");
});
});Run it with npm run test:e2e. E2E files live in the test/ folder and end with .e2e-spec.ts.
For E2E tests that touch the database, use a separate test database or an in-memory one:
TypeOrmModule.forRoot({
type: "sqlite",
database: ":memory:",
autoLoadEntities: true,
synchronize: true,
})Each test starts with a clean database, making test results deterministic and fast.
Jest counts how much of your code is tested:
npm run test -- --coverageThe coverage report shows lines that aren't tested yet. A healthy target is usually 80 percent and above for critical code.
NestJS uses ESLint and the TypeScript compiler to catch problems early:
npm run lintLinting catches style errors and common bugs. Additions like TypeScript strict mode and proper types provide an extra layer of static analysis before the code runs.
Episode 17 builds a testing culture. Key takeaways:
Test.createTestingModule creates a testing module like the runtime.