Skip to content

mongodb memory server 를 이용한 단위 테스트 시 mongoDB 의존성 제거

Min-h-96 edited this page Nov 28, 2022 · 4 revisions

Service 계층의 로직들이 정상적으로 동작하는지 테스트 하기 위해서는 DB 에 접근을 해야할 때가 있습니다.

하지만, 테스트를 위해 실제 사용하고 있는 DB 에 접근하는 것은 말도 안됩니다. 현재 배포되고 있는 서비스라면 테스트를 하다가 어떤 문제가 발생할 수 도 있으니까요.

mongodb-memory-server 패키지를 사용하면, 실제 DB 에 접근하지는 않지만 구현된 Repository 계층의 로직과 함께 MongoDB 를 이용할 수 있습니다.

mongodb-memory-server?

mongodb-memory-server 패키지는 테스트용 MongoDB 서버를 키는 패키지입니다. 예시와 함께 어떻게 활용할 수 있는지 살펴보겠습니다.

// user.service.spec.ts

describe('UserService', () => {
  let userService: UserService;
  let userRepository: UserRepository;
  let mongod: MongoMemoryServer;
  let mongoConnection: Connection;
  let userModel: Model<User>;

  beforeAll(async () => {
    mongod = await MongoMemoryServer.create();
    const uri = mongod.getUri();
    mongoConnection = (await connect(uri)).connection;
    userModel = mongoConnection.model(User.name, userSchema);
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
	UserRepository,
	{
          provide: getModelToken(User.name),
          useValue: userModel,
        },
      ],
    }).compile();

    userService = module.get<UserService>(UserService);
    userRepository = module.get<UserRepository>(UserRepository);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

위 처럼 코드를 작성해서 테스트용 MongoDB 서버를 사용할 수 있습니다.

차례대로 설명을 드리면,

  • MongoMemoryServer.create() 는 테스트용 MongoDB 서버를 만들고, 데몬을 가져옵니다.
  • getUri() 를 이용하여 MongoDB 에 접근하기 위한 uri 를 가져옵니다.
  • uri 를 이용하여 MongoDB 에 연결하고, 그 연결을 mongoConnection 이라는 값으로 가져옵니다.
  • mongoConnection 을 통해서, 우리가 조작하고자 하는 모델을 얻습니다.
  • 마지막으로 getModelToken() 을 이용하여, 모델을 주입합니다.

이 다음으로 beforeAll 을 이용하여 테스트용 MongoDB 서버를 실행시켰으니, 모든 테스트가 끝난 이후에 실행시킨 MongoDB 서버를 종료시키는 로직이 필요합니다. 이 로직은 afterAllafterEach 안에 나눠서 작성됩니다.

// user.service.spec.ts

describe('UserService', () => {
  // ... 중략 ...

  afterAll(async () => {
    await mongoConnection.dropDatabase();
    await mongoConnection.close();
    await mongod.stop();
  }

  afterEach(async () => {
    const collections = mongoConnection.collections;
    for (const key in collections) {
      const collection = collections[key];
      await collection.deleteMany({});
    }
  });
};
  • afterAll 블록에서 모든 데이터베이스를 삭제하고, 연결을 종료하고, 데몬을 중지하는 코드입니다.
  • afterEach 에서는 각각의 개별 테스트 후에, 컬렉션에 생성된 모든 항목(document)을 삭제하는 구문입니다. 실제 사용된 컬렉션을 User 하나이지만, 더 일반적으로 동작할 수 있게끔 코드가 짜여져있습니다.

이제 테스트를 위한 기본 설정이 끝이 났습니다. 이를 이용하여 테스트를 진행해보겠습니다.

it('테스트', async () => {
  const userData = {
    authProvider: 'google',
    authId: '53361445',
    email: '[email protected]',
    username: 'minchoi',
  };
  const user = await userRepository.create(userData);

  const foundUser = await userService.findOne(
    userData.authProvider,
	  userData.authId,
	);

  expect(foundUser._id).toEqual(user._id);
});

테스트는 실제 프로덕트 코드에서 User 를 생성하기 위해 필요한 데이터로 직접 userRepository.create() 를 이용해서 User 를 생성하고, 잘 생성됐는지 userService.findOne() 함수로 찾은 User 와 동일한 지를 확인하는 테스트 입니다.

Reference

얼리버드

프로젝트

개발일지

스프린트 계획

멘토링

데일리 스크럼

데일리 개인 회고

위클리 그룹 회고

스터디

Clone this wiki locally