Spring Data JPA 사용하는 미션에서 멘토님께 리뷰를 받으며 다음과 같은 피드백을 받으며 슬라이스 테스트 존재에 대해 알게 되었다.
슬라이스 테스트란 무엇이고 왜 사용하는 것일까??
@SpringBootTest
대신 슬라이스 테스트를 하는 이유는 무엇일까??단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트이며 로버트 마틴의 클린코드에서 깨끗한 테스트를 위한 다섯 가지 F.I.R.S.T 규칙을 말한다.
@SpringBootTest
어노테이션을 사용하는 경우 단점은 아래와 같다.
Bean
을 로드하기 때문에 시간이 오래걸리고 무겁다.따라서 repository 레이어의 단위테스트의 경우 @SpringBootTest
대신 @DataJpaTest
사용하여 테스트를 작성하는 경우 통해 속도적인 측면과 의존성 측면에서 이점을 가질 수 있다.
아래는 대표적인 슬라이스 테스트 어노테이션이 존재하는데 해당 글에서는 중 @WebMvcTest
, @DataJpaTest
살펴보도록 할 것이다.
@WebMvcTest
@WebFluxTest
@DataJpaTest
@JsonTest
@RestClientTest
@WebMvcTest(ShelterPostController.class)
public class ShelterPostControllerTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@MockBean
protected ShelterPostService shelterPostService;
@Test
@DisplayName("게시글 리스트 조회 테스트")
void getShelterPostsTest() throws Exception {
// given, when, then
...
}
}
spring-boot-test 패키지는 Mockito를 포함하고 있기 때문에 기존에 사용하던 방식대로 Mock 객체를 생성해서 테스트하는 방법도 있지만, spring-boot-test에서는 새로운 방법도 제공한다.
해당 어노테이션은 테스트 내용 중 외부 서비스를 호출하는 부분을 Mock해서 쉽게 처리할 수 있다.
@SpringBootTest
public class XXXControllerTest {
@MockBean // 외부 서비스 호출에 사용되는 RestTemplate Bean을 Mock
private RestTemplate mockRT;
@MockBean // 외부 서비스 호출에 사용되는 Service Bean을 Mock
private XXXService xXXService;
}
Spring Data JPA를 테스트하고자 한다면 @DataJpaTest
기능을 사용해볼 수 있다.
@Entity
클래스를 스캔한다.@DataJpaTest
는 @Transactional
어노테이션을 포함하고 있다.
만약 @Transactional
기능이 필요하지 않다면 아래와 같이 줄 수 있다.
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class SomejpaTest {
...
}
@DataJpaTest
기능을 사용하면 @Entity
를 스캔하고 repository를 설정하는 것 이외에도 테스트를 위한 TestEntityManager
라는 빈이 생성된다.
@DataJpaTest
class SomejpaTest {
@Autowired
private TestEntityManager entityManager;
@Test
@DisplayName("게시글 아이디로 댓글 목록 삭제 테스트")
void deleteAllByMissingPostIdTest() {
// given
LongStream.rangeClosed(1, 3).forEach(idx ->
entityManager.persist(Comment.builder()
.missingPost(missingPost)
.content("내용")
.account(account)
.build()
)
);
// when
commentRepository.deleteAllByMissingPostId(missingPost.getId());
List<Comment> comments = commentRepository.findAll();
// then
SoftAssertions.assertSoftly(softAssertions -> {
softAssertions.assertThat(comments).hasSize(3);
comments.forEach(foundComment -> softAssertions.assertThat(foundComment.isDeleted()).isTrue());
}
);
}
}
만약 테스트에 내장된 임베디드 데이터베이스를 사용하지 않고 real database를 사용하고자 하는 경우, @AutoConfigureTestDatabase
어노테이션을 사용하면 손쉽게 설정할 수 있습니다.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SomejpaTest {
...
}
슬라이스 테스트 시, 하위 레이어는 Mock
기반으로 만들기 때문에 주의할 점들이 있다.