Spring Data JPA 사용하는 미션에서 멘토님께 리뷰를 받으며 다음과 같은 피드백을 받으며 슬라이스 테스트 존재에 대해 알게 되었다.
- Repository Test시 @SpringBootTest를 @DataJpaTest로 변경해서 테스트 작성하기
슬라이스 테스트란 무엇이고 왜 사용하는 것일까??
Spring Boot 슬라이스 테스트
슬라이스 테스트란?
- 즉 스프링은 레이어 별로 잘라서 특정 레이어에 대해서 Bean을 최소한으로 등록시켜 테스트 하고자 하는 부분에 최대한 단위 테스트를 지원해주고 있다.
- 그렇다면
@SpringBootTest
대신 슬라이스 테스트를 하는 이유는 무엇일까??
F.I.R.S.T 테스트 원칙
단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트이며 로버트 마틴의 클린코드에서 깨끗한 테스트를 위한 다섯 가지 F.I.R.S.T 규칙을 말한다.
- Fast — 테스트는 빨라야 한다.
- Isolated — 각 테스트는 서로 의존하면 안된다.
- Repeatable — 테스트는 어떤 환경에서도 반복 가능해야 한다.
- Self-validating — 테스트는 bool 값으로 결과를 내야 한다.
- Timely — 테스트는 적시에 작성해야 한다.
@SpringBootTest
어노테이션을 사용하는 경우 단점은 아래와 같다.
- 실제 구동되는 애플리케이션의 설정, 모든
Bean
을 로드하기 때문에 시간이 오래걸리고 무겁다. - 테스트 단위가 크기 때문에 디버깅이 어려운 편이다.
- 외부 API 콜같은 Rollback 처리가 안되는 테스트 진행을 하기 어려움
따라서 repository 레이어의 단위테스트의 경우 @SpringBootTest
대신 @DataJpaTest
사용하여 테스트를 작성하는 경우 통해 속도적인 측면과 의존성 측면에서 이점을 가질 수 있다.
슬라이스 테스트 어노테이션 종류
아래는 대표적인 슬라이스 테스트 어노테이션이 존재하는데 해당 글에서는 중 @WebMvcTest
, @DataJpaTest
살펴보도록 할 것이다.
@WebMvcTest
@WebFluxTest
@DataJpaTest
@JsonTest
@RestClientTest
@WebMvcTest
- MVC를 위한 테스트.
- 웹에서 테스트하기 힘든 컨트롤러를 테스트하는 데 적합.
- 웹상에서 요청과 응답에 대해 테스트할 수 있음.
- 시큐리티, 필터까지 자동으로 테스트하며, 수동으로 추가/삭제 가능.
- @SpringBootTest 어노테이션보다 가볍게 테스트할 수 있음.
- 다음과 같은 내용만 스캔하도록 제한함.@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor,
- 따라서 의존성이 끊기기 때문에, 예를 들면 서비스와 같은 객체들은 @MockBean을 사용해서 만들어 사용해야 한다.
@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
...
}
}
@MockBean
spring-boot-test 패키지는 Mockito를 포함하고 있기 때문에 기존에 사용하던 방식대로 Mock 객체를 생성해서 테스트하는 방법도 있지만, spring-boot-test에서는 새로운 방법도 제공한다.
- 바로 @MockBean 어노테이션을 사용해서 이름 그대로 Mock 객체를 빈으로써 등록할 수 있다.
- 기존에 사용되던 스프링 Bean이 아닌 Mock Bean을 주입한다.
- 그렇기 때문에 만일 @MockBean으로 선언된 빈을 주입받는다면 Spring의 ApplicationContext는 Mock 객체를 주입한다.
- 새롭게 @MockBean을 선언하면 Mock 객체를 빈으로써 등록하지만, 만일 @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock 빈으로 대체된다.
해당 어노테이션은 테스트 내용 중 외부 서비스를 호출하는 부분을 Mock해서 쉽게 처리할 수 있다.
@SpringBootTest
public class XXXControllerTest {
@MockBean // 외부 서비스 호출에 사용되는 RestTemplate Bean을 Mock
private RestTemplate mockRT;
@MockBean // 외부 서비스 호출에 사용되는 Service Bean을 Mock
private XXXService xXXService;
}
@DataJpaTest
Spring Data JPA를 테스트하고자 한다면 @DataJpaTest
기능을 사용해볼 수 있다.
- 해당 테스트는 기본적으로 in-memory embedded database를 생성하고
@Entity
클래스를 스캔한다. - 일반적인 다른 컴포넌트들은 스캔하지 않는다. 따라서 특정 bean의 의존성이 필요한 경우 아래의 방법 사용
- @import
- @DataJpaTest(includeFilters = @Filter(..))
@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
기반으로 만들기 때문에 주의할 점들이 있다.
- 의존성 객체를 Mocking하기 때문에 완벽한 테스트는 아님
- Mocking 처리하기 위한 시간이 소요
- Mocking 라이브러리에 대한 학습 비용 발생
- Mock 기반 으로 테스트하기 때문에 실제 환경에서는 결과가 다를 수 있음
참고 출처
- https://github.com/cheese10yun/spring-guide/blob/master/docs/test-guide.md#단점
- https://tecoble.techcourse.co.kr/post/2021-05-18-slice-test/
- https://meetup.toast.com/posts/124
- https://github.com/HomoEfficio/dev-tips/blob/master/Spring-Boot-Test-외부-서비스-호출-with-%40MockBean.md
- https://jojoldu.tistory.com/226
- https://lalwr.blogspot.com/2019/09/spring-test.html
'spring' 카테고리의 다른 글
WebClient의 DataBufferLimitException 해결방법 (0) | 2024.11.15 |
---|---|
TestExecutionListener를 이용한 테스트 격리 방법 (1) | 2024.11.05 |
Spring Async (0) | 2024.08.02 |
Spring Webclient (0) | 2024.08.02 |
Spring AOP (0) | 2024.08.02 |