스프링부트 2.2 버전부터 테스트 프레임워크가 JUnit4에서 JUnit5로 버전업 되면서 여러가지 변경사항이 존재한다.
// @ExtendWith 어노테이션이 테스트 메타 어노테이션에 포함되어 있음
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(org.springframework.boot.test.context.SpringBootTestContextBootstrapper.class)
@ExtendWith({org.springframework.test.context.junit.jupiter.SpringExtension.class})
public @interface SpringBootTest extends annotation.Annotation
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper.class)
@ExtendWith({org.springframework.test.context.junit.jupiter.SpringExtension.class})
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters({org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTypeExcludeFilter.class})
@AutoConfigureCache
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@ImportAutoConfiguration
public @interface WebMvcTest extends annotation.Annotation
생성자를 통한 의존성 주입은 @Autowired
을 생략하더라도 스프링 컨테이너가 의존성 주입을 해준다.
하지만 JUnit 테스트시 의존성 주입을 JUnit이 스프링 컨테이너 앞에서 먼저 개입한다. 이때 스프링 컨터이너에 의존성 주입을 요청해야만 스프링 컨테이너가 개입할 수 있다. 만약 그렇지 않는다면 No ParameterResolver registered for parameter 에러가 발생한다.
resolver : 해결사
생성자 파라미터가 무엇인지 알수없어 해결할 방법이 없다는 의미의 에러라고 생각됨.
따라서 생성자 기반 주입시 @Autowired가 생략될 수 없다.
자바와 다르게 코틀린은 생성자를 클래스 선언과 동시에 정의하기 때문에 여러가지 해결책이 있다.
@WebMvcTest
class FooTest @Autowired constructor(private val mvc: MockMvc){
...
}
@WebMvcTest
class FooTest (@Autowired private val mvc: MockMvc){
...
}
@WebMvcTest
class FooTest {
@Autowired
private lateinit var mvc: MockMvc
}
@WebMvcTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class FooTest(private val mvc: MockMvc) {
....
}
// application.property
spring.test.constructor.autowire.mode=all
참고
SpringBoot + JUnit에서 의존성 주입하기
JUnit 5 + Kotlin 테스트 클래스에서 생성자 주입 이슈
처음 통합테스트를 접했을 때 프로그램이 구동해서 작성한 테스트 모두를 구동하는 테스트라고 생각했었다. JUnit의 통합테스트는 스프링의 모든 빈을 로드하는 프로덕션 환경과 동일한 환경에서 수행되는 테스트를 뜻한다.
@SpringBootTest
어노테이션을 통해 통합테스트를 할 수 있다. 기본적으로 내장 톰캣이 띄워지지 않고 서블릿을 모킹하여 테스트가 수행된다. webEnvironment 값에 따라 내장 톰캣의 구동 여부와 테스트 환경이 달라진다.
기본값은 WebEnvironment.MOCK이며 통합테스트의 수행 환경을 설정한다.
내장 톰캣이 아닌 모킹한 서블릿 환경에서 테스트를 수행한다.
랜덤하거나 지정한 포트 번호로 내장 톰캣을 구동하여 테스트를 수행한다. 이 경우 TestRestTemplate 또는 WebTestClient를 사용하여 요청을 보내고 응답을 받는다.
통합 테스트 환경에서 스프링 MVC에 대해 테스트하려면 @AutoConfigureMockMvc
어노테이션이 필요하다. 이 어노테이션을 사용할 경우 MockMvc와 WebTestClient를 사용할 수 있고 요청을 보내고 응답을 받을 수 있다.
단위 테스트는 특정 기능을 테스트하기 위한 방법이고 스프링부트는 특정 기능 테스트에 대한 다양한 자동 설정 테스트를 지원한다. 자세한 내용은 스프링부트 테스트 공식 문서를 참고.
@WebMvcTest
는 스프링 MVC에 대한 단위 테스트를 지원한다. @SpringBootTest
+ @AutoConfigureMockMvc
를 이용한 스프링 MVC 테스트에 비해 WebMvcTest
가 가지는 장점은 가볍고 빠르다는 것이다.
따로 설정하지 않더라도 @WebMvcTest
는 @Controller
, @ControllerAdvice
, @JsonComponent
, Converter
, GenericConverter
, Filter
, HandlerInterceptor
, WebMvcConfigurer
, WebMvcRegistrations
,HandlerMethodArgumentResolver
만 로드하기 때문에 모든 빈을 로드하는 통합테스트 환경보다 가볍다.
만약 controllers 와 같은 옵션으로 로드할 빈을 지정한다면 더욱 빠르고 가벼운 테스트를 진행할 수 있다. 옵션에 대한 자세한 내용은 스프링 공식문서 를 참고.
@SpringBootTest + @AutoConfigureMockMvc는 그럼 왜 쓸까?
스프링 MVC에 대한 가볍고 빠른 기능 테스트가 존재하는데 굳이 무거운 통합 테스트가 필요한 이유는 무엇일까.
@WebMvcTest
는 웹 레이어에 해당하는 빈만 로드하므로@Service
,@Repository
같은 다른 계층의 빈은 로드하지 못한다. 따라서@MockBean
을 통해 해당 빈을 모킹해야만 한다.@SpringBootTest
+@AutoConfigureMockMvc
는 모든 계층의 빈을 다 로드하므로 모킹할 필요가 없다. 통합적인 동작의 수행을 테스트한다면 통합테스트를 통한 MVC 테스트를 하면 된다.
참고
깔끔하게 정리 되어 이해가 잘 되네요!
감사합니다.