티스토리 뷰
[Spring] 스프링부트 테스트를 위한 의존성과 어노테이션, 애플리케이션 컨택스트 캐싱(@SpringBootTest, @WebMvcTest, @DataJpaTest)
망나니개발자 2022. 4. 22. 10:00스프링부트에서 테스트를 작성하기 위한 다양한 어노테이션(@SpringBootTest, @WebMvcTest, @DataJpaTest)들을 알아보도록 하겠습니다. 실제 테스트를 작성하는 방법은 이 포스팅을 참고해주세요.
1. 스프링부트 테스트를 위한 의존성
[ 스프링부트 테스트를 위한 의존성 ]
스프링부트는 서블릿 기반의 웹 개발을 위한 spring-boot-starter-web, 유효성 검증을 위한 spring-boot-starter-validation 등 spring-boot-starter 의존성을 제공하고 있다. 테스트를 위한 spring-boot-starter-test 역시 존재하는데, 다음과 같은 라이브러리들이 포함된다.
- JUnit 5: 자바 애플리케이션의 단위 테스트를 위한 사실상의 표준 테스트 도구
- Spring Test & Spring Boot Test: 스프링 부트 애플리케이션에 대한 유틸리티 및 통합 테스트 지원
- AssertJ: 유연한 검증 라이브러리
- Hamcrest: 객체 Matcher를 위한 라이브러리
- Mockito: 자바 모킹 프레임워크
- JSONassert: JSON 검증을 위한 도구
- JsonPath: JSON용 XPath
spring-boot-starter-test 의존성을 추가하면 스프링이 미리 만들어둔 테스트를 위한 다양한 어노테이션을 사용해 편리하게 테스트를 작성할 수 있다. 이번에는 스프링부트가 제공하는 다양한 테스트 어노테이션을 살펴보도록 하자.
[ 스프링부트 테스트를 위한 어노테이션 ]
스프링부트는 spring-boot-test-autoconfigure를 통해 특정 어노테이션을 붙여주면 해당 테스트를 위한 설정들을 자동으로 제공해준다. 대표적으로 다음과 같은 어노테이션들을 제공하고 있다.
- @SpringBootTest
- @WebMvcTest
- @DataJpaTest
- @RestClientTest
- @JsonTest
- @JdbcTest
- 기타 등등
[ @Autowired와 @MockBean, @SpyBean ]
스프링부트가 제공하는 테스트 어노테이션들은 실제 스프링 컨텍스트를 사용해 빈들을 등록한다. 그러므로 @Autowired로 등록된 빈을 주입받을 수 있다. 특정 객체를 모킹하고 싶은 경우에는 @MockBean과 @SpyBean를 사용하면 된다.
- @MockBean: 해당 객체를 Mock된 빈으로 등록함
- @SpyBean: 해당 객체를 Spy된 빈으로 등록함
만약 @MockBean으로 선언한 빈이 없다면 Mock 객체를 빈으로 등록하지만, 동일한 타입과 이름의 빈이 존재하면 해당 빈은 Mock 빈으로 대체된다. @SpyBean은 스프링의 프록시가 적용된 빈이라면 사용이 불가능하니 프록시를 제거해주어야 한다.
@MockBean과 @SpyBean의 사용은 테스트 성능에 영향을 줄 수 있는데, 이와 관련해서는 다음 포스팅에서 자세히 살펴보도록 하자.
2. 스프링부트 테스트를 위한 다양한 어노테이션들
(@SpringBootTest, @WebMvcTest, @DataJpaTest)
[ 통합테스트를 위한 @SpringBootTest 어노테이션 ]
@SpringBootTest
@SpringBootTest를 사용하면 손쉽게 통합 테스트를 위한 환경을 준비해준다. @SpringBootTest는 모든 빈들을 스캔하고 애플리케이션 컨텍스트를 생성하여 테스트를 실행한다. @SpringBootTest의 어노테이션에는 다양한 값을 줄 수 있는데, 이를 살펴보면 다음과 같다.
- value와 properties: 애플리케이션 실행에 필요한 프로퍼티를 key=value 형태로 추가할 수 있음
- args: 애플리케이션의 arguments로 값을 전달할 수 있음
- classes: 애플리케이션을 로딩할 때 사용되는 컴포넌트 클래스들을 정의할 수 있음
- webEnvironment: 웹 테스트 환경을 설정할 수 있음
예를 들어 프로퍼티 같은 경우에는 다음과 같이 설정할 수 있다.
@SpringBootTest(properties = { "mangkyu.blog=tistory" })
@SpringBootTest 어노테이션의 webEnvironment
webEnvironment는 enum이며 총 4가지 값을 가지고 있는데, 각각의 기능과 특징을 정리하면 다음과 같다.
- MOCK
- 웹 기반의 애플리케이션 컨텍스트를 생성하지만 MOCK 환경으로 제공하여 내장 서버가 시작되지 않음
- 웹 환경이 클래스패스에 없다면 웹이 아닌 애플리케이션 컨텍스트를 생성함
- 웹 기반의 Mock 테스트를 위해 @AutoConfigureMockMvc 또는 @AutoConfigureWebTestClient와 함께 사용할 수 있음
- RANDOM_PORT
- 웹 기반의 애플리케이션 컨텍스트를 생성하여 실제 웹 환경을 제공함
- 내장 서버도 실행되며 사용하지 않는 랜덤 포트를 listen함
- DEFINED_PORT
- 웹 기반의 애플리케이션 컨텍스트를 생성하고 실제 웹 환경을 제공함
- 내장 서버도 실행되며 지정한 포트(default 8080)를 listen함
- NONE
- SppringApplication로 애플리케이션 컨텍스트를 생성함
- 하지만 mock이나 다른 것들을 포함해 어떠한 웹 환경도 제공하지 않음
webEnvironment의 기본값은 MOCK이므로 실제 웹 서버가 실행되지는 않는다. 또한 스프링은 @Transactional이 있으면 테스트가 끝나고 트랜잭션을 롤백하는데, RANDOM_PORT나 DEFINED_PORT를 사용하면 별도의 쓰레드에서 실제 서버가 구동되어 롤백되지 않는다. 그래서 테스트가 격리되지 않아 실패할 수 있는데, 이와 관련해서는 이 포스팅을 참고해 해결하도록 하자. 또한 RANDOM_PORT나 DEFINED_PORT를 사용하면 TestRestTemplate이 의존성으로 추가되므로 API 호출이 필요할 때 이용할 수 있다.
@SpringBootTest의 단점과 슬라이스 테스트(Slice Test)
@SpringBootTest는 기본적으로 모든 빈을 탐색하고 등록한다. 그래서 특정 계층만 테스트가 필요한 상황에서 @SpringBootTest를 사용하면 불필요하게 무거워지고 시간이 오래 걸린다. 그래서 스프링은 특정 부분만 테스트할 수 있는 슬라이스 테스트(SliceTest)를 위한 어노테이션들을 제공하는데, 이에 대해 알아보도록 하자. 물론 슬라이스 테스트도 스프링 컨텍스트를 구성하므로 통합 테스트이다.
[ 컨트롤러 테스트를 위한 @WebMvcTest 어노테이션 ]
@WebMvcTest
@WebMvcTest를 사용하면 손쉽게 컨트롤러를 테스트할 수 있는 환경을 준비해주는데, 내장된 서블릿 컨테이너가 랜덤 포트로 실행된다. @WebMvcTest는 애플리케이션 컨텍스트를 만들 때 컨트롤러와 연관된 빈들만을 제한적으로 찾아서 등록한다. 그러므로 일반적인 @Component나 @ConfigurationProperties 빈들은 스캔되지 않는다.
- @Controller, @RestController
- @ControllerAdvice, @RestControllerAdvice
- @JsonComponent
- Filter
- WebMvcConfigurer
- HandlerMethodArgumentResolver
- 기타 등등
추가적인 설정이 필요하면 @Import를 사용할 수 있고, @MockBean이나 @SpyBean 역시 사용할 수 있다. 또한 @WebMvcTest는 컨트롤러 테스트이므로 @WebMvcTest 내부에 @AutoConfigureMockMvc가 들어있다. 그러므로 @Autowired로 MockMvc를 주입받을 수 있으며, 만약 웹플럭스를 이용중이라면 @WebFluxTest를 사용하면 된다.
@WebMvcTest 어노테이션의 value와 controllers
@WebMvcTest에는 특정 컨트롤러만을 빈으로 등록하도록 제한할 수 있다.
@WebMvcTest(UserController.class)
class UserControllerTest {
}
이를 통해 특정 컨트롤러만 테스트 가능하도록 하는데, 해당 컨트롤러가 의존하는 빈이 있다면 @MockBean이나 @SpyBean을 사용해주어야 한다. 문제는 이렇게 특정 컨트롤러만을 빈으로 띄우고 @MockBean과 @SpyBean으로 특정 빈을 바꾸는 것은 새로운 애플리케이션 컨택스트를 필요로 한다. 그래서 애플리케이션 컨텍스트의 수를 증가시키므로 주의해야 하는데, 아래에서 다시 한번 살펴보도록 하자.
[ JPA 레포지토리 테스트를 위한 @DataJpaTest 어노테이션 ]
@DataJpaTest
JPA 레포지토리 테스트를 위해서는 @DataJpaTest를 이용할 수 있다. @DataJpaTest는 기본적으로 @Entity가 있는 엔티티 클래스들을 스캔하며 테스트를 위한 TestEntityManager를 사용해 JPA 레포지토리들을 설정해준다. 마찬가지로 @Component나 @ConfigurationProperties 빈들은 스캔되지 않는다.
@DataJpaTest의 기본적인 동작
앞서 설명하였듯 스프링은 테스트에 @Transactional이 있으면 테스트가 끝난 후 자동으로 트랜잭션을 롤백한다. @DataJpaTest에는 @Transactional 어노테이션이 들어있어서 기본적으로 모든 테스트가 롤백된다. 만약 롤백을 원하지 않는다면 @Rollback(false)를 추가하면 된다.
@DataJpaTest
@Rollback(false)
class MyRepositoryTests {
// ...
}
또한 만약 H2와 같은 내장 데이터베이스가 클래스 패스에 존재한다면 내장 데이터베이스가 자동 구성된다. spring-boot-test 의존성에는 기본적으로 H2가 들어있으므로 별다른 설정을 주지 않는다면 H2로 설정된다. 내장 데이터베이스로 설정되기를 원하지 않는다면 다음과 같이 AutoConfigureTestDatabase의 replace 속성을 NONE으로 주면 된다.
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {
// ...
}
3. 스프링 부트 테스트의 애플리케이션 컨텍스트 캐싱
[ 애플리케이션 컨텍스트 캐싱 ]
스프링 부트가 제공하는 테스트는 모두 애플리케이션 컨텍스트를 구성해주어야 한다. 하지만 모든 테스트마다 이를 구성하려면 비용이 커지므로 스프링은 테스트 시에 내부적으로 스프링 컨텍스트를 캐싱해두고 동일한 설정이라면 재사용한다. 그러므로 다음과 같이 애플리케이션 컨텍스트 설정에 변경을 주는 기능들은 테스트 시에 새로운 컨텍스트를 생성하도록 요구한다.
- @MockBean, @SpyBean
- @TestPropertySource
- @ConditionalOnX
- @WebMvcTest에 컨트롤러 지정
- @Import
- 기타 등등
@MockBean과 @SpyBean은 특정 빈을 Mock이 적용된 빈으로 등록한다. 그러므로 애플리케이션 컨텍스트가 갖는 빈이 달라져 새로운 컨텍스트를 생성하게 된다. 그러다보니 @MockBean과 @SpyBean을 많이 사용하면 테스트가 느려질 수 있으며 캐싱된 애플리케이션 컨텍스트의 수를 증가시킨다. 물론 테스트 클래스에서 @MockBean과 @SpyBean이 적용된 객체가 완전히 동일하다면 재사용한다.
그러므로 만약 현재 테스트 속도가 느리다면 테스트들마다 애플리케이션 컨텍스트가 생성되는지 확인해보도록 하자.
[ 스프링 부트 테스트 어노테이션 정리 및 요약 ]
여기서 주의해야 할 점은 슬라이스 테스트가 단위 테스트는 아니라는 점이다. 해당 어노테이션으로 테스트를 진행하면 테스트를 위한 애플리케이션 컨텍스트가 준비된다. 즉, 스프링이 준비되므로 해당 테스트들은 통합 테스트에 해당한다.
추가로 위에서 언급한 슬라이스 테스트 어노테이션 외에도 @JsonTest, @RestClientTest, @DataJdbcTest 등도 있다. 만약 json 관련 테스트를 위해 gson이나 objectMapper 등의 의존성이 필요하다면 @JsonTest를, RestTemplate이 필요하다면 @RestClientTest를 사용할 수 있다. 또한 Datasource와 JdbcTemplate만 필요하다면 @JdbcTest를 이용하면 된다.
이번에는 스프링부트에서 테스트를 작성하기 위한 다양한 어노테이션(@SpringBootTest, @WebMvcTest, @DataJpaTest)들을 알아보았습니다. 실제 테스트를 작성하는 방법은 이 포스팅을 참고해주세요!
또한 스프링 부트가 제공하는 테스트는 통합 테스트인만큼 주의사항들이 있습니다. 이어지는 포스팅에서는 테스트가 느려지지 않도록 스프링 부트 설정 주의사항과 테스트 코드 작성 시에 참고할만한 내용을 다뤄보도록 하겠습니다.
관련 포스팅
- 스프링부트 테스트를 위한 의존성과 어노테이션, 애플리케이션 컨택스트 캐싱(@SpringBootTest, @WebMvcTest, @DataJpaTest)
- 스프링 부트 설정/테스트 작성 시의 주의사항(스프링 부트 테스트가 오래 걸리는 이유)