출시·고도화 중
NestJS · Express 안내서 · 5/6
nest new로 만든 프로젝트에는 Jest와 Supertest, Nest의 테스트 도구 @nestjs/testing이 이미 설정되어 있습니다. 이 장에서는 서비스와 컨트롤러의 단위 테스트, 애플리케이션 전체를 띄우는 e2e 테스트, 그리고 Express 앱을 Supertest로 검사하는 방법을 다룹니다.
단위 테스트는 src 안의 *.spec.ts 파일이고, e2e 테스트는 test 폴더의 *.e2e-spec.ts 파일입니다. e2e 테스트는 test/jest-e2e.json이라는 별도 Jest 설정을 씁니다.
npm run test # 단위 테스트
npm run test:watch # 파일이 바뀔 때마다 다시 실행
npm run test:cov # 커버리지 보고서
npm run test:e2e # e2e 테스트의존성이 없는 서비스는 평범한 클래스처럼 new로 만들어 테스트해도 됩니다. 하지만 의존성이 있다면 Nest의 Test.createTestingModule로 테스트용 모듈을 만들고, 실제 프로바이더 대신 가짜 객체를 넣는 것이 편합니다.
// src/users/users.service.spec.ts
import { Test } from "@nestjs/testing";
import { getRepositoryToken } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { UsersService } from "./users.service";
describe("UsersService", () => {
let service: UsersService;
const repository = { find: jest.fn(), create: jest.fn(), save: jest.fn() };
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [UsersService, { provide: getRepositoryToken(User), useValue: repository }],
}).compile();
service = moduleRef.get(UsersService);
});
it("returns every user", async () => {
repository.find.mockResolvedValue([{ id: 1, name: "Kim" }]);
await expect(service.findAll()).resolves.toHaveLength(1);
});
});getRepositoryToken(User)는 @InjectRepository(User)가 찾는 주입 토큰입니다. 이렇게 하면 데이터베이스 없이 서비스의 논리만 검사할 수 있습니다.
컨트롤러를 테스트할 때는 서비스를 가짜로 바꿉니다. 모듈 전체를 가져온 뒤 일부만 바꾸고 싶다면 overrideProvider를 씁니다.
// src/users/users.controller.spec.ts
import { Test } from "@nestjs/testing";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
describe("UsersController", () => {
it("delegates to the service", async () => {
const usersService = { findAll: jest.fn().mockResolvedValue([]) };
const moduleRef = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService],
})
.overrideProvider(UsersService)
.useValue(usersService)
.compile();
const controller = moduleRef.get(UsersController);
await controller.findAll();
expect(usersService.findAll).toHaveBeenCalled();
});
});e2e 테스트는 실제 모듈로 애플리케이션을 만들고, Supertest로 HTTP 요청을 보내 응답을 검사합니다. main.ts에서 등록한 전역 파이프 같은 설정은 테스트용 앱에도 똑같이 적용해야 합니다.
// test/users.e2e-spec.ts
import { INestApplication, ValidationPipe } from "@nestjs/common";
import { Test } from "@nestjs/testing";
import request from "supertest";
import { AppModule } from "../src/app.module";
describe("Users (e2e)", () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({ imports: [AppModule] }).compile();
app = moduleRef.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.init();
});
afterAll(async () => {
await app.close();
});
it("rejects an invalid body", () => {
return request(app.getHttpServer()).post("/users").send({ name: "" }).expect(400);
});
});app.getHttpServer()는 Nest가 감싼 실제 HTTP 서버를 돌려주므로 포트를 따로 열 필요가 없습니다. 데이터베이스를 쓰는 e2e 테스트는 테스트 전용 데이터베이스를 두거나, Testcontainers 같은 도구로 테스트 때마다 컨테이너를 띄워 운영 데이터와 분리합니다.
Express도 같은 방식입니다. 앞 장처럼 app.js가 앱을 내보내기만 하면, Supertest가 그 앱에 직접 요청을 보냅니다.
npm i -D jest supertest// test/users.test.js
const request = require("supertest");
const app = require("../src/app");
test("GET /users/1 returns the user", async () => {
const response = await request(app).get("/users/1").expect(200);
expect(response.body.id).toBe("1");
});Node.js에 들어 있는 테스트 실행기 node:test와 Supertest를 함께 써도 됩니다.
Test.createTestingModule로 테스트 모듈을 만들고 useValue · overrideProvider로 의존성을 가짜로 바꿉니다.createNestApplication()과 app.getHttpServer()로 실제 요청 흐름을 검사하며, 전역 설정도 똑같이 적용합니다.listen을 분리해 두면 Supertest로 쉽게 테스트할 수 있습니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.