[NestJS 테스트코드] Postgres, MongoDB 환경에서 유닛 & E2E 테스트 제대로 해보기

2025. 7. 9. 17:03·Nest.js
728x90
반응형

NestJS를 쓰면서 테스트 코드를 생각한다면 자연스럽게 jest 기반 테스트 환경이 따라온다.
이번엔 뜨도록 프로젝트가 어느정도 안정되어 미루고 미뤄뒀던 테스트 코드 작성을 끝낸 기념으로 이 글을 작성한다.
근데 생각보다 복잡하다. 내경우에 postgres, mongoDB를 사용중인데,

  • - SQLite 썼다가 타입 오류에 머리 싸매고
  • - Mongo는 또 Mongo대로 memory-server 세팅이 다르고...
     
    그래서 정리했다.

의존성 설치부터 pg-mem + mongodb-memory-server까지.


기본 테스트 환경 구성

설치

npm install --save-dev jest @nestjs/testing ts-jest @types/jest
  • - jest: 테스트 실행기
  • - @nestjs/testing: NestJS 전용 유닛테스트 헬퍼
  • - ts-jest: TypeScript로 작성된 테스트를 jest에서 돌릴 수 있게 함

만약 Mongoose도 함께 사용한다면:

npm install --save-dev mongodb-memory-server

est.config.ts 설정

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

Nest CLI 쓰면 자동으로 생성되지만 커스텀 설정할 땐 꼭 확인 필요.

test용 환경 설정

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

E2E용 설정은 따로 관리하는 게 좋다.

E2E: End to End로 "사용자가 사용하는 상황을 가정하고 테스트 하는 것"을 의미한다.

유닛 테스트 예시

describe('UserService', () => {
  let service: UserService;
  let repo: Repository<User>;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [UserService],
    }).compile();

    service = moduleRef.get<UserService>(UserService);
  });

  it('회원 생성 테스트', async () => {
    const result = await service.createUser({ name: '철수' });
    expect(result.name).toBe('철수');
  });
});

E2E 테스트 예시

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

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

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

  it('/auth/login (POST)', () => {
    return request(app.getHttpServer())
      .post('/auth/login')
      .send({ email: 'test@test.com', password: '1234' })
      .expect(201);
  });
});

PostgreSQL 테스트에서 삽질했던 이야기

유닛 테스트는 mock으로도 충분히 커버되지만, E2E 테스트에서는 실제 DB까지 통합 검증하고 싶어서 sqlite in-memory로 먼저 설정했었음.
처음엔 typeorm + sqlite 조합으로 테스트하려 했는데...

@Column({
  type: 'enum',
  enum: MyEnum,
})
myField: MyEnum;

이게 SQLite에서는 안 먹힌다.
심지어 uuid_generate_v4() 같은 함수도 없어서 에러 터짐.
그래서 다음과 같은 에러를 보게 된다:

no such function: uuid_generate_v4

해결책은?
pg-mem으로 가자.

pg-mem으로 PostgreSQL 완벽 대체

설치

npm install pg-mem

사용 이유:

  • - SQLite는 PostgreSQL과 호환되지 않는 기능이 너무 많아서, 실제와 유사한 환경이 필요했고

  • - pg-mem은 PostgreSQL을 JS로 시뮬레이션하는 in-memory DB라서 실제 enum, uuid, array 등의 기능을 테스트할 수 있음

  • - 물론 실제 PostgreSQL과 100% 동일하게 동작하지는 않지만 테스트코드 작성시엔 도움이 된다.

    사용 예시:

import { newDb } from 'pg-mem';

export function createInMemoryDataSource(entities: any[]): DataSource {
  const db = newDb({ autoCreateForeignKeyIndices: true });

  db.public.registerFunction({
    name: 'uuid_generate_v4',
    args: [],
    implementation: () => '00000000-0000-0000-0000-000000000000',
  });

  return db.adapters.createTypeormDataSource({
    type: 'postgres',
    entities,
    synchronize: true,
  });
}
  • · pg-mem은 실제 PostgreSQL 기능을 흉내내는 in-memory DB

  • · TypeORM 호환, 속도 빠르고 안정적

  • · enum, uuid, array 등 다 된다!

    주의할 점

  • · test 환경에서만 사용

    실제 PostgreSQL과 100% 같진 않으니 일부 기능은 제한적일 수 있음

MongoDB는? → mongodb-memory-server

Mongo는 따로!
mongodb-memory-server를 쓰면 깔끔하게 해결된다.

import { MongoMemoryServer } from 'mongodb-memory-server';

let mongod: MongoMemoryServer;

beforeAll(async () => {
  mongod = await MongoMemoryServer.create();
  const uri = mongod.getUri();

  await mongoose.connect(uri);
});

afterAll(async () => {
  await mongoose.disconnect();
  await mongod.stop();
});

NestJS에서 MongooseModule.forRoot(uri)에 연결해주면 끝.


마무리

스프링에서 H2 같은 DB를 쓰다가 실제 운영 DB에서 오류 나는 경험이 있다면 이해될 거다. NestJS도 마찬가지로, 가짜 SQLite로 테스트할 바엔 pg-mem으로 PostgreSQL 환경을 흉내 내는 게 훨씬 신뢰도 높다.

728x90
반응형

'Nest.js' 카테고리의 다른 글

[NestJS - 트러블 슈팅] 몽고 DB replicaSet 없이는 트랜잭션 불가능  (4) 2025.06.19
NestJS 엔티티, 왜 다 public이야? – Spring 개발자의 궁금증 해결  (1) 2025.05.25
[NestJS 인증 흐름] Jwt 토큰 기반 인증 요청 흐름 정리  (1) 2025.05.22
[NestJS 응답/요청 변환기] 내부 camelCase, 외부 snake_case 통일하기  (0) 2025.05.22
[Nest.js] 제로베이스에서 1주일, 실전까지 반나절  (1) 2025.05.22
'Nest.js' 카테고리의 다른 글
  • [NestJS - 트러블 슈팅] 몽고 DB replicaSet 없이는 트랜잭션 불가능
  • NestJS 엔티티, 왜 다 public이야? – Spring 개발자의 궁금증 해결
  • [NestJS 인증 흐름] Jwt 토큰 기반 인증 요청 흐름 정리
  • [NestJS 응답/요청 변환기] 내부 camelCase, 외부 snake_case 통일하기
highgarden
highgarden
커밋 하나하나가 쌓여 커다란 정원이 되는 중입니다. 하루하루 정성껏 심어가는 중 https://github.com/highgarden7
  • highgarden
    커밋심는 정원
    highgarden
  • 전체
    오늘
    어제
    • 분류 전체보기 (37)
      • ai (1)
      • devops (2)
      • Nest.js (14)
      • linux (14)
      • 네트워크 (6)
      • git (0)
      • aws (0)
      • docker (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    IP
    네트워크
    springboot
    Chat GPT
    nestjs
    E2E
    Java
    Linux
    githib action
    vercel
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
highgarden
[NestJS 테스트코드] Postgres, MongoDB 환경에서 유닛 & E2E 테스트 제대로 해보기
상단으로

티스토리툴바