NestJS/jest (테스트 코드)

[NestJS] Jest 단위 테스트 - Mocking

Yuco 2023. 6. 12. 22:07

1. 모킹(Mocking)이란?

 

자바스크립트 테스팅 프레임워크로 jest를 사용할 경우, 장점 중 하나는 다른 라이브러리의 설치 없이 바로 mock 기능을 사용할 수 있다는 것입니다.

mock(모킹)이란, 테스트하려는 코드의 외부 의존성이 존재할 때 모조품을 만들어 일단 돌아가게 하는 것입니다.

즉, 단위 테스트를 작성할 때 해당 코드가 의존하는 부분을 가짜(mock) 으로 대체하는 기법입니다. 

예를 들면, 데이터베이스에 직접 데이터를 저장하거나, 삭제하는 코드에 대한 단위 테스트를 작성할 때 실제 데이터베이스를 사용하게 된다면 아래 예시 처럼 여러 가지 문제점이 발생할 수 있습니다.

 

- 데이터베이스 접속과 같이 I/O 작업이 포함되는 테스트 케이스를 작성하게 될 경우 실행 속도가 현저히 떨어질 수 밖에 없습니다.

- 특히, 프로젝트의 규모가 커질수록 한 번에 실행되어야 할 테스트 케이스가 많아지기 때문에 작은 속도 저하들은 큰 이슈가 될 수 있습니다.

- 테스트 자체를 위한 코드보다 데이터베이스와 연결하고, 트랜잭션을 생성하고, 쿼리를 전송하는 코드가 더 길어질 수 있습니다. (이는 '특정 기능만 분리해서 테스트 하겠다'는 단위 테스트의 근본적인 사상에 부합하지 않습니다.)

- 테스트 실행 시 데이터베이스가 오프라인 작업중이라면 해당 테스트는 실패하게 됩니다. 따라서 테스트가 인프라 환경에 영향을 받게 됩니다.

- 테스트 종료 직후, 데이터베이스를 이전 상태로 복구하거나, 롤백 해줘야 하는 상당히 번거로운 작업이 될 수 있습니다.

 

이러한 이슈들을 방지하기 위해,  jest는 단위 테스트를 작성할 때 실제 객체의 역할을 하는 가짜 객체를 생성하는 모킹 매커니즘을 제공하게 됩니다. 또한 이는 테스트가 실행되는 동안 가짜 객체에 어떤 일들이 발생했는지 기억하기 때문에 가짜 객체가 내부적으로 어떻게 사용되는 지 검증도 가능합니다. 

 

결론적으로, 모킹을 이용하면 실제 객체를 사용하는 것보다 훨씬 빠르고 가볍게 실행되면서도, 항상 동일한 결과를 출력하는 테스트를 작성할 수 있습니다. 

 

1. jest.fn()

- jest는 가짜 함수(mock function)를 생성할 수 있도록 jest.fn() 함수를 제공합니다.

 

 const MockRepository = () => ({
 create: jest.fn(),
 save: jest.fn(),
 findOne: jest.fn(),
 })

 

-이는 데이터베이스(Repository)에 접근하는 코드를 테스트하기 위해 객체를 생성하고(create), 저장하고(save), 데이터베이스에서 값을 찾아 리턴하는(findOne) 가짜 함수를 사용할 수 있도록 모킹한 코드입니다. 

 

2. jest.spyOn()

- 모킹에는 spy(스파이)라는 개념이 있습니다. 이는 테스트를 작성할 때 어떤 객체가 속한 함수의 구현을 가짜로 대체하지 않고, 해당 함수의 호출 여부와 어떻게 호출되었는지만을 알아야 할 때 사용합니다.

 

 jest.spyOn(userRepository'create').mockResolvedValue(user// userLog 생성 및 저장
 jest.spyOn(userRepository'save').mockResolvedValue(user)  

 

- 이는 실제로 데이터베이스에 접근하지 않고도, 데이터베이스(userRepository)에 객체를 생성하고, user 라는 객체를 저장하도록 모킹하는 코드를 작성할 수 있습니다. 이는 단위 테스트이기 때문에 실제 데이터베이스에는 당연히 저장되지 않고, mockResolvedValue()를 통해 실제 리턴되는 값을 지정함으로써 함수의 동작을 테스트할 수 있습니다. (userRepository도 당연히 모킹해야 하는 부분입니다.)

 

 expect(userRepository.create).toHaveBeenCalledWith(user) // param: user
 expect(userRepository.save).toHaveBeenCalled()

 

- expect를 통해 실제 userRepository.create 가 실행 되었는지(toHaveBeenCalled()), 실행 될 때 어떤 parameter가 넘어갔는지(toHaveBeenCalledWith()) 를 테스트할 수 있습니다. 

 

 

* 처음으로 nestJS의 테스트 코드를 작성해보며, 적은 글입니다. 혹시 잘못된 부분이 있다면 댓글로 친절하게 알려주세요 😀