서비스 계층에서 테스트 하기 위해서 실제 Repository를 DI 하게 된다면 단위 테스트 를 작성하기 위한 F.I.R.S.T 원칙을 위배하게 된다.
테스트 어노테이션
@SPringBootTest : 통합테스트, 전체 Bean 전체
@WebMvcTest : 단위테스트, MVC 테스트 , MVC 와 관련된 Bean
@DataJpaTest : 단위테스트, JPA 테스트 , JPA와 관련된 Bean
@RestClientTest : 단위테스트, REST API 테스트 , 일부 Bean
@JsonTest : 단위테스트, Json 테스트, 일부 Bean
@SpringBootTest
@RunWith(SpringRunner.class)와 함께 통합테스트를 위한 스프링 부트 테스트 어노테이션으로 어플리케이션 실행 설정을 바꿔서 테스트 하는 것이 가능하며, 실제 구동되는 어플리케이션과 동일한 컨텍스트를 가져 일대일 대응 수준의 테스트가 가능하다. 따라서 테스트를 실행하기 위해 어플리케이션 설정을 모두 로드하기 때문에 규모가 클수록 느리게 작동된다.
Mokito
테스트에 들어가는 객체의 동작을 직접 제어할 수 있도록 하는 가짜객체 를 지원하는 테스트 프레임 워크이다. Mock객체의 의존성을 주입함으로서 분리되고 독립적인 테스트 코드 작성이 가능해진다. SpringBootTest에서 Mockito를 사용하고 싶을 때에는 @ExtendWith(MockitoExtension.class) 어노테이션을 추가하여 사용하도록 한다.
Mock 객체 의존성 주입
가짜 객체를 사용하기 위해서는 다음과 같은 어노테이션을 사용한다.
가짜 객체를 주입했다면 다음과 같은 메소드를 사용하여 가짜 결과를 반환하도록 지정해준다. ( = Stub )
doReturn(java.util.Optional.of(activity)).when(activityRepository).findById(anyInt());
when(activityRepository.findById(anyInt())).thenReturn(java.util.Optional.of(activity));
여기서 doReturn과 thenReturn의 차이는 다음과 같다.
Mock객체 적용
@RunWith(SpringRunner.class)
@ExtendWith(MockitoExtension.class)
@SpringBootTest
@Transactional
class UserActivityServiceTests {
@InjectMocks
private UserActivityService userActivityService;
@MockBean
private UserActivityRepository userActivityRepository;
@MockBean
private ActivityRepository activityRepository;
@MockBean
private UserRepository userRepository;
//...
@Test
@DisplayName("주어진 상태에 따른 새로운 사용자 액티비티를 생성하고 사용자 레벨을 조정한다.")
void create_user_activity() {
when(userRepository.findById(anyInt())).thenReturn(java.util.Optional.of(user));
doReturn(java.util.Optional.of(activity)).when(activityRepository).findById(anyInt());
// when(activityRepository.findById(anyInt())).thenReturn(java.util.Optional.of(activity));
UserActivityResponse response = userActivityService.create(userActivityRequest);
//then
assertEquals...
}
@Test
@DisplayName("범위 밖의 점수로는 리뷰를 생성하지 않는다.")
void 예외경우 테스트(){
assertThatThrownBy(() -> reviewService.create(request))
.isInstanceOf(InvalidRatingException.class);
}
}
위와 같이 가짜 객체를 사용할 대상에 @Mock 어노테이션을, 가짜 객체를 주입할 대상에 (테스트 할 대상) @InjectsMocks를 해줌으로서 독립적이고 반복가능한 테스트를 작성할 수 있다.