JUnit 테스트 with Kotlin

tkppp·2022년 1월 23일
5

SpringBoot with Kotlin

목록 보기
8/12

JUnit5

스프링부트 2.2 버전부터 테스트 프레임워크가 JUnit4에서 JUnit5로 버전업 되면서 여러가지 변경사항이 존재한다.

  1. @RunWith -> @ExtendWith
  2. 메타 어노테이션의 사용시 @ExtendWith 생략 가능(@SpringBootTest, @WebMvcTest)
  // @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가 생략될 수 없다.

코틀린에서의 의존성 주입

자바와 다르게 코틀린은 생성자를 클래스 선언과 동시에 정의하기 때문에 여러가지 해결책이 있다.

주 생성자에 @Autowired 선언

@WebMvcTest
class FooTest @Autowired constructor(private val mvc: MockMvc){
    ...
}

생성자의 각 파라미터에 @Autowired 선언

@WebMvcTest
class FooTest (@Autowired private val mvc: MockMvc){
    ...
}

lateinit을 이용한 필드 주입

@WebMvcTest
class FooTest {
    @Autowired
    private lateinit var mvc: MockMvc
}

자바, 코틀린 공통 해결책

@TestConstructor 어노테이션으로 AutowireMode를 변경해 @Autowired 생략

@WebMvcTest
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class FooTest(private val mvc: MockMvc) {
    ....
}

.property 파일에서 AutowireMode를 변경해 @Autowired 생략

// application.property
spring.test.constructor.autowire.mode=all

참고

SpringBoot + JUnit에서 의존성 주입하기
JUnit 5 + Kotlin 테스트 클래스에서 생성자 주입 이슈

JUnit 통합 테스트

처음 통합테스트를 접했을 때 프로그램이 구동해서 작성한 테스트 모두를 구동하는 테스트라고 생각했었다. JUnit의 통합테스트는 스프링의 모든 빈을 로드하는 프로덕션 환경과 동일한 환경에서 수행되는 테스트를 뜻한다.

@SpringBootTest

@SpringBootTest 어노테이션을 통해 통합테스트를 할 수 있다. 기본적으로 내장 톰캣이 띄워지지 않고 서블릿을 모킹하여 테스트가 수행된다. webEnvironment 값에 따라 내장 톰캣의 구동 여부와 테스트 환경이 달라진다.

webEnvironment

기본값은 WebEnvironment.MOCK이며 통합테스트의 수행 환경을 설정한다.

WebEnvironment.MOCK

내장 톰캣이 아닌 모킹한 서블릿 환경에서 테스트를 수행한다.

WebEnvironment.RANDOM_PORT or DEFINED_PORT

랜덤하거나 지정한 포트 번호로 내장 톰캣을 구동하여 테스트를 수행한다. 이 경우 TestRestTemplate 또는 WebTestClient를 사용하여 요청을 보내고 응답을 받는다.

@AutoConfigureMockMvc

통합 테스트 환경에서 스프링 MVC에 대해 테스트하려면 @AutoConfigureMockMvc 어노테이션이 필요하다. 이 어노테이션을 사용할 경우 MockMvcWebTestClient를 사용할 수 있고 요청을 보내고 응답을 받을 수 있다.

JUnit 단위 테스트

단위 테스트는 특정 기능을 테스트하기 위한 방법이고 스프링부트는 특정 기능 테스트에 대한 다양한 자동 설정 테스트를 지원한다. 자세한 내용은 스프링부트 테스트 공식 문서를 참고.

@WebMvcTest

@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 테스트를 하면 된다.

참고

Spring Boot - 통합테스트와 단위테스트
스프링부트 공식문서 - 테스트

1개의 댓글

comment-user-thumbnail
2022년 8월 29일

깔끔하게 정리 되어 이해가 잘 되네요!
감사합니다.

답글 달기