스프링부트에선 크게 2가지 모듈이 웹 애플리케이션 테스트를 위한 기능을 제공해줍니다.
spring boot 프로젝트에서 spring-boot-starter-test 라는 dependency를 적용하면 다음과 같은 의존성을 자동으로 설정해 줍니다.
Unit Test는 통합테스트가 아니므로 @SpringBootTest 어노테이션을 통해 스프링을 실행시키지 않고, 테스트하려는 메서드에 필요한 부분만 최소한으로 사용하여 테스트를 실행하여야 합니다.
그렇기 하기 위해선 Mockito 라는 것이 필요합니다. Mockito는 가짜 객체를 의미하는 Mock을 생성하여 의존성을 최소화시켜줄 수 있는 테스트 프레임워크 입니다. Mockito 를 사용하면 테스트 대상에서 필요로 하는 객체들을 임의로 생성하여 원하는 시나리오대로 테스트를 쉽게 구성할 수 있습니다.
Mockito 사용 방법
의존성 주입(DI)
의존성 주입이 필요한 Mock 객체를 만들기 위해선 3가지 어노테이션이 주로 사용됩니다.
ReviewService를 테스트 한다고 했을 때, ReviewRepository 객체를 주입받아야 한다면, ReviewRespository를 @Mock 어노테이션으로 선언하고, ReviewService를 @InjectsMock 어노테이션으로 선언함으로써 테스트를 할 수 있습니다.
Stub
Mock 객체로 만들어진 객체를 통해 시나리오에 맞는 원하는 결과를 만들어 놓는 행위를 Stub이라고 합니다. Stub을 하기 위해서 Mockito에서 주로 사용되는 메서드는 다음과 같습니다.
ReviewRepository의 메서드 호출 결과로 예외를 발생시키고 싶은 경우엔 doThrow() 메서드를 사용하여 테스트를 진행할 수 있습니다.
Junit5 에서의 Mockito 확장
Junit5를 활용한 Unit 테스트 코드를 작성하는 클래스에서 Mockito를 사용하기 위해선 @ExtendWith(MockitoExtension.class) 코드를 통해 Mockito 사용을 확장하여야 합니다.
Junit4 의 경우 @RunWith 어노테이션을 통해 Mockito 를 사용할 수 있었지만, Spring Boot 버전이 업그레이드됨에 따라 Junit5를 지원하여 @ExtendWith 어노테이션을 통해 Mockito를 사용할 수 있습니다. @RunWith의 차이점으로는 메타 어노테이션으로 사용할 수 있어 @SpringBootTest 어노테이션 내부엔 @ExtendWith을 사용하는 것을 알 수 있습니다.
Unit 테스트를 작성하기 위해 필요한 기본적인 개념 및 도구에 대해서 알아봤습니다. 다음 글부터 기본 원칙 및 한계점을 알아보고 레이어별 테스트 코드를 작성해보도록 하겠습니다!
Unit 테스트 코드 작성 시, 5가지 원칙은 다음과 같습니다.
F - fast : 테스트 코드는 빠르게 실행되어야 한다.
I - Independent : 독립적으로 실행되어야 한다.
R - Repeatable : 반복 가능해야 한다.
S - Self Validating : 스스로 테스트 검증 가능해야 한다
T - Timely : 프로덕션 코드 직전에 작성되어야 한다.(TDD)
질이 좋은 테스트 코드와 목적에 맞는 테스트 코드를 작성하기 위해선 위 규칙을 준수하면 좋지만, 반드시 위 규칙에 얽매여서 헤맬 필요없이 자신의 테스트 목적에 맞게 코드를 작성하는 것이 더욱 중요하다고 생각합니다.
Unit Test(단위 테스트)는 개발자가 구현한 일련의 메서드들을 독립적인 입장에서 시나리오에 대한 검증을 하기 위해선 반드시 필요한 테스트입니다.
하지만 Spring Boot를 사용하여 웹 어플리케이션 서버를 구축하였다면 보다 넓은 관점에서의 시나리오를 검증하는 것 또한 서버에 대한 신뢰성을 더 높이는 방법입니다. 그렇게 하기 위해선 ‘통합 테스트’라는 과정이 필요합니다. 통합 테스트에 대해서는 다음 포스팅에서 다뤄 보도록 해보겠습니다 😒
일반적으로 테스트 코드를 작성할 때 Given - When - Then (준비, 실행, 검증) 패턴을 활용하면 더 쉽게 코드를 작성할 수 있습니다.
Mockito vs BDDMockito
Mockito를 사용하여 Given - When - Then 패턴을 적용한 코드를 작성할 때 살짝 거슬리는 메서드가 있습니다. 바로 Mockito.when() 메서드인데, 해당 메서드는 given(준비) 과정에 어울리는 메서드입니다.
위 불편함을 해소하기 위해 나온 클래스가 있는데 바로 BDDMockito 클래스입니다.
BDDMockito는 Mockito 클래스를 상속하여 Mockito와 기능 측면에선 별 다른 부분이 없고, 위에 언급되었던 메서드 이름에서의 직관성을 높여주기 위해 메서드 이름을 변경해준 클래스입니다.
그 예로, Mockito.when()
은 given()
으로 변경하였고 Mockito.verify()
는 when().should()
로 변경하였습니다.
(개인적으로 verify()
가 when().should()
보다 더 직관성이 높아 보입니다 😅)
만약 Given - When - Then 패턴을 적용한 코드의 직관성을 높이기 위해선 BDDMockito 클래스를 사용하는 것도 좋은 방법 중 하나일 것 같습니다.
import static org.mockito.BDDMockito.*;
Writing Your F.I.R.S.T Unit Tests - DZone Java
[junit5] Mock을 이용한 단위 테스트 (@InjectMocks 과 @Mock 차이)
dev-tips/Spring-Boot-레이어별-테스트.md at master · HomoEfficio/dev-tips