버그 픽스, 리팩토링 등 좋은 의도에서 코드를 수정했을지 몰라도 개발자의 실수로 잘 돌아가던 코드가 에러를 발생시킬 가능성이 항상 도사리고 있다. 그렇다면 코드가 변경되었을 때 의도한 대로 동작하는지 확인할 수 있는 방법은 없을까?
테스트를 이용하면 코드의 신뢰성을 확인할 수 있다. 코드를 수정한 후 기존에 작성해놓았던 테스트 코드가 실패한다면 이는 코드가 제대로 동작하지 않는다는 걸 의미한다. 즉, 테스트는 새로운 기능을 추가하거나 기존 기능을 변경할 때 예상치 못한 문제를 방지하는 중요한 수단이다.
테스트는 쉽고 빠르게 실행할 수 있어야 한다. 테스트 실행이 번거롭고 오래 걸린다면 개발자들은 테스트하는 것을 기피할 것이기 때문이다. 이런 점에서 Jest는 큰 이점을 갖고 있다고 판단하여 테스트 라이브러리로 Jest를 선택했다.
<aside> 💡 Jest의 장점
jest로 서비스 코드를 테스트 할 것이다. 하지만 서비스 코드 안에는 데이터베이스에 접근하는 로직이 들어있다.
...
@Injectable()
export class TarotService {
constructor(
@InjectRepository(TarotCard)
private readonly tarotCardRepository: Repository<TarotCard>, // 데이터베이스에 접근하는 레포지토리!!
@InjectRepository(TarotResult)
private readonly tarotResultRepository: Repository<TarotResult>, // 데이터베이스에 접근하는 레포지토리!!
) {}
...
}
데이터베이스를 조작하면 실제 프로세스에 부정적인 결과를 초래할 수 있다. 따라서 데이터베이스를 테스트 코드로부터 보호해야 한다.
위 문제를 해결하기 위해 목(mock)을 활용할 수 있다. 목은 함수에 대한 호출만 기록하고 어떠한 일도 수행하지 않는 것을 의미한다. 즉, 레포지토리 함수가 호출되긴 하나 데이터베이스에 접근하지 않는 것이다. 이처럼 목을 활용하면 의존성을 시뮬레이션할 수 있다.
it('해당 번호의 타로 카드가 존재하지 않아 NotFoundException을 반환한다.', async () => {
[{ cardNo: -1 }, { cardNo: 79 }].forEach(async ({ cardNo }) => {
const findOneByMock = jest
.spyOn(tarotCardRepository, 'findOneBy')
.mockResolvedValueOnce(null);
await expect(
tarotService.findTarotCardByCardNo(cardNo),
).rejects.toThrow(NotFoundException);
expect(findOneByMock).toHaveBeenCalledWith({
cardNo: cardNo,
cardPack: undefined,
});
});
});
위의 테스트 코드는 타로 카드를 조회하는 메서드 findTarotCardByCardNo
에 대한 테스트를 진행하고 있다. spyOn
을 이용하여 레포지토리의 findOneBy
메서드를 목으로 대체한다. 그리고 mockResolvedValueOnce
함수를 이용하여 findOneBy
가 리턴할 값을 지정한다. 이로써 데이터베이스에 접근하지 않고 서비스 코드를 테스트 할 수 있다.