-
Notifications
You must be signed in to change notification settings - Fork 0
NestJS에서 통합 테스트를 해보자!
프로젝트를 진행하면서 개발한 로직들의 테스트 코드를 작성하는 것은 많은 이점이 있습니다.
그 중에서 SuperTest 라이브러리를 이용한 통합 테스트 방법에 대해서 알아보고자 합니다.
SuperTest 는 내부적으로 NestJS 또는 express 서버를 구동시켜서 가상의 요청을 보낸 뒤 결과를 검증할 수 있습니다.
SuperTest 라이브러리를 사용하는 가장 큰 이유는 NestJS 로 프로젝트를 구성했을 때, 기본적으로 제공하는 라이브러리이면서 공식 문서의 Testing 파트에서 Http 요청을 포함한 테스트를 진행할 때 사용하는 방법을 이야기하고 있습니다.
npm i -D supertest @types/supertest
설치하기 위한 명렁어지만, NestJS 로 프로젝트를 구성하면 기본적으로 설치되어있습니다.
예시를 통해 사용 방법을 알아보기에 앞서서, 참고 사항을 말씀드리겠습니다.
- 기본적으로 SuperTest + Jest 방식으로 코드를 작성했습니다. 기본적인 Jest 를 이용하기 위한 틀에 SuperTest 의 기능을 추가했다고 생각하시면 될 것 같습니다.
- 예시에서 통합 테스트하고자 하는 내용은 저희 프로젝트의 Like 모듈(controller + service + repository)입니다.
- DB와 연동하여 통합 테스트를 하기 위해서는 실제 DB를 사용할 수는 없었습니다. 저희 프로젝트는 MongoDB 를 사용하고 있기 때문에, Repository Layer 의 동작까지 확인하기 위해서는
mongodb-memory-server
패키지로 in-memory-db를 활용했습니다. 해당 패키지를 사용하는 방법은 다음 글을 참고하시면 좋을 것 같습니다.
// like.integration.spec.ts
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
describe('Like 모듈 통합 테스트', () => {
let app: INestApplication;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [LikeModule],
}).compile();
app = module.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('GET /api/like/:id', () => {
return request(app.getHttpServer()).get('/api/like/1').expect(200);
});
}
위 코드가 SuperTest 를 이용하기 위한 가장 기본적인 틀 입니다.
SuperTest 는 위에서 말한 것처럼 내부적으로 테스트를 위한 서버를 구동시켜서, 가상의 요청을 보냄으로써 올바른 응답이 오는 지를 테스트하는 라이브러리입니다.
따라서 위 코드를 보면 NestJS 서버를 동작시키기 위해서 app.module.ts
에 있는 AppModule
을 이용해서 main.ts
에서 app
을 설정해서 서버를 구동하는 로직과 굉장히 흡사한 것을 알 수 있습니다.
순서대로 설명하면 다음과 같습니다.
-
Test.createTestingModule().compile()
은 테스트 하고자 하는LikeModule
을 import 받아서 테스트용 module 을 만듭니다. -
app = module.createNestApplication()
은 테스트용 모듈을 가지고 내부적으로 테스트용 서버를 구동시킵니다.
일단, 여기까지 작성하고 테스트를 동작시키면 다음과 같은 에러가 발생합니다.
이 에러가 발생한 이유를 살펴보면,
Nest can't resolve dependencies of the LikeModel
라고 적혀있는 걸 확인하실 수 있습니다.
LikeModule
이 LikeModel
을 의존하고 있지만, 그 의존성을 확인할 수 없어서 에러가 발생하는 것입니다. 실제 LikeModule
를 확인하면 LikeModel
과 UserModule
을 의존하고, UserModule
은 UserModel
을 의존하고 있는 것을 확인할 수 있습니다.
// like.module.ts
@Module({
imports: [
MongooseModule.forFeature([{ name: Like.name, schema: likeSchema }]),
forwardRef(() => UserModule),
],
// ... 중략 ...
})
export class LikeModule {}
// user.module.ts
@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: userSchema }]),
// ... 중략 ...
})
export class UserModule {}
즉, LikeModule
을 테스트하기 위해서는 LikeModel
과 UserModel
의 의존성을 해결해야 합니다.
그것을 해결하는 방법은 다음과 같습니다. NestJS 공식문서에 따르면, 의존성을 제공하기 위해서 overrideProvider()
와 useValue()
메소드를 활용합니다. 해당 메소드가 Jest 를 이용한 단위 테스트에서 providers
에 역할을 대신한다고 생각됩니다.
// like.integration.spec.ts
// ... 중략 ...
const module = await Test.createTestingModule({
imports: [LikeModule],
})
.overrideProvider(getModelToken(User.name))
.useValue(userModel)
.overrideProvider(getModelToken(Like.name))
.useValue(likeModel)
.compile();
// ... 중략 ...
위 코드처럼 LikeModel
과 UserModel
의 의존성을 제공할 수 있습니다.
이 상태로 npm run test
를 진행하면 다음과 같은 결과를 얻을 수 있습니다.
이제 SuperTest 를 이용한 통합 테스트 준비가 끝났습니다.
하지만 추가로 해결해야 하는 문제가 있었습니다. 저희는 세션을 통한 인증을 하고 있습니다. 그렇기 때문에 세션에 로그인 정보가 없다면 해당 API를 테스트할 수가 없었습니다.
이를 해결하고자 기존 express에서 세션을 설정해주는 방식을 적용했습니다.
app.use((req, res, next) => {
req.session = {
userId: CREATE_USER.STUB1._id.toString(),
};
next();
});
await app.init();
해당 코드를 통해 api 요청 전에 미리 세션에 원하는 값을 넣어서 정상적인 테스트를 수행할 수 있었습니다.
- 📃 기획서
- 📂 Backlog
- 📊 ERD, 폴더 구조
- 🗓️ 회의록