개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

NestJS Testing — 단위 테스트와 E2E 테스트

2025년 3월 24일
NestJSTestingJestTestingModuleE2E

NestJS는 테스트를 언어 수준의 기능처럼 다룬다. @nestjs/testing 패키지가 제공하는 TestingModule을 사용하면, 실제 애플리케이션과 동일한 DI 컨테이너를 테스트 환경에서 그대로 재현할 수 있다. 덕분에 Mock을 주입하더라도 프로덕션 코드 구조를 바꿀 필요가 없고, 테스트 자체가 설계의 품질을 드러낸다.

TestingModule 구조

[ createTestingModule() ]
        │
        ▼
┌───────────────────────┐
│   Testing DI Container │
│  ┌─────────────────┐  │
│  │   Provider A    │  │
│  │   Provider B    │  │  ← Mock / Real 교체 가능
│  │   Provider C    │  │
│  └─────────────────┘  │
└───────────────────────┘
        │
        ▼
   moduleRef.get(Token)

createTestingModule 은 @Module 데코레이터와 동일한 메타데이터 구조를 받아 격리된 DI 컨테이너를 만든다. compile 을 호출한 뒤 moduleRef.get 으로 인스턴스를 꺼낼 수 있다.

TestingModule — @nestjs/testing이 제공하는 테스트 전용 NestJS 모듈. 실제 HTTP 서버 없이 DI만 구동한다.

단위 테스트 (Unit Test)

의존성을 Mock Provider로 교체해 클래스 하나의 로직만 검증한다.

// cats.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatsService } from './cats.service';
import { CatsRepository } from './cats.repository';

describe('CatsService', () => {
  let service: CatsService;
  const mockRepo = { findAll: jest.fn().mockResolvedValue([]) };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CatsService,
        { provide: CatsRepository, useValue: mockRepo },
      ],
    }).compile();

    service = module.get<CatsService>(CatsService);
  });

  it('고양이 목록을 반환한다', async () => {
    const result = await service.findAll();
    expect(mockRepo.findAll).toHaveBeenCalledTimes(1);
    expect(result).toEqual([]);
  });
});

useValue로 실제 Repository 대신 Mock 객체를 주입하면 DB 연결 없이 Service 로직만 테스트할 수 있다.

Mock Provider — useValue / useFactory / useClass 로 실제 구현체를 대체하는 테스트 전용 Provider.

통합 테스트 (Integration Test)

여러 Provider를 함께 구동해 모듈 경계를 검증한다. 실제 TypeORM Repository 대신 인메모리 DB를 연결하거나, jest.spyOn으로 외부 호출만 차단하는 방식이 일반적이다.

전략장점단점
인메모리 DB (SQLite)DB 로직까지 검증방언 차이 발생 가능
Mock Repository빠른 실행SQL 쿼리 미검증
실제 DB + 트랜잭션 롤백정확도 최고환경 의존성 높음

E2E 테스트

@nestjs/testing과 supertest를 결합해 HTTP 계층 전체를 검증한다.

// cats.e2e-spec.ts
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { AppModule } from '../src/app.module';
import { INestApplication } from '@nestjs/common';

describe('Cats (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleRef.createNestApplication();
    await app.init();
  });

  it('GET /cats → 200', () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200);
  });

  afterAll(() => app.close());
});

createNestApplication을 호출하면 실제 HTTP 서버가 기동된다. supertest는 포트 없이 내부 소켓으로 요청을 보내므로 포트 충돌이 없다.

supertest — HTTP 서버를 실제로 listen하지 않고 내부 소켓으로 요청을 전달하는 테스트 유틸리티.

Jest 설정

NestJS CLI가 생성하는 기본 jest 설정은 package.json에 포함된다.

{
  "jest": {
    "moduleFileExtensions": ["js", "json", "ts"],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": { "^.+\\.(t|j)s$": "ts-jest" },
    "collectCoverageFrom": ["**/*.(t|j)s"],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}

E2E 테스트는 별도 test/jest-e2e.json을 두고 rootDir를 test로 지정하며, testRegex를 \\.e2e-spec\\.ts$로 변경한다. npm run test:e2e로 분리 실행하는 것이 관례다.

← 이전 글NestJS Configuration — 환경 변수와 설정을 관리하는 방법
다음 글 →NestJS 프로젝트 구조 — 실무 폴더 구성 방법