저번시간에 이어 오늘도 junit5 포스팅이다. 오늘은 junit5로 Spring을 어떻게 Test 하는지 알아보자. 이미 spring에서 만들어 놓아서 우리는 사용하기만 하면 된다. 일단 아래와 같이 maven 디펜더시를 받자.

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.0.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.0.RELEASE</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.0.1</version>
    <scope>test</scope>
  </dependency>
</dependencies>

간단하게 어떻게 하는지만 테스트를 할 예정이라 web과 관련된 설정은 하지 않았다. 그렇다고 웹이라고 해서 크게 다른 건 없다.
테스트를 위해 샘플 코드를 작성해보자.

public interface HelloService {

  String hello();
}

@Configuration
public class Application {

  @Bean
  HelloService helloService() {
    return () -> "hello world";
  }
}

우리는 HelloService 라는 인터페이스를 빈으로 등록 하였다. 샘플 코드이기에 크게 의미는 없다.
저번시간에 배운 ParameterResolver 인터페이스를 구현한 Spring 의 코드가 존재한다. 바로 SpringExtension 클래스이다. 뿐만아니라 여러 인터페이스를 구현했으니 참고해도 되겠다. 우리는 SpringExtension 클래스를 사용해서 테스트를 할 것이다. 일단 한번 작성해보자.

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = Application.class)
class GeneralHelloServiceTests {

  private final HelloService helloService;

  @Autowired
  GeneralHelloServiceTests(HelloService helloService) {
    this.helloService = helloService;
  }

  @Test
  void parametersHello(@Autowired HelloService helloService) {
    assertEquals(helloService.hello(), "hello world");
  }
  @Test
  void constructorHello() {
    assertEquals(this.helloService.hello(), "hello world");
  }

}

junit5의 ExtendWith 어노테이션을 사용해서 우리는 확장할 수 있었다. 또한 기존의 ContextConfiguration 어노테이션으로 컨텍스트를 설정 할 수 있었다. SpringExtension를 사용하면 생성자를 통해 빈을 주입 받을 수 있고 파라미터를 통해서도 빈을 주입 받을 수 있다. 하지만 여기서 주의할 점은 spring 4.3 부터는 일반적인 코드에는 생성자 위에 @Autowired를 붙이지 않아도 자동으로 주입하지만 테스트에서는 그렇지 않다. 무조건 @Autowired를 작성해줘야한다. 물론 파라미터도 마찬가지다. 우리는 Spring에서 미리 만들어진의 junit5 지원으로 아주 심플하게 테스트를 할 수 있게 되었다.

이 보다 좀 더 간단하게 테스트 할 수 있는 방법이다. 간단하다고 하더라도 그냥 한줄 정도? Spring에서 지원해주는 @SpringJUnitConfig 어노테이션은 위의 2개의 애노테이션을 합친 애노테이션이다. 그래서 2줄을 1줄로 바꿀 수 있는 좋은(?) 애노테이션이다. 한번 해보자.

@SpringJUnitConfig(Application.class)
class AnnotationHelloServiceTests {

  private final HelloService helloService;

  @Autowired
  AnnotationHelloServiceTests(HelloService helloService) {
    this.helloService = helloService;
  }

  @Test
  void parametersHello(@Autowired HelloService helloService) {
    assertEquals(helloService.hello(), "hello world");
  }

  @Test
  void constructorHello(@Autowired HelloService helloService) {
    assertEquals(this.helloService.hello(), "hello world");
  }
}

위와 같이 작성해도 기존과 동일한 코드로 동작한다. @SpringJUnitConfig 애노테이션 말고도 web을 지원해주는 @SpringJUnitWebConfig 애노테이션도 존재한다. 이것은 한번씩 해보도록 하자. 그렇다고 크게 어려운건 없고 @WebAppConfiguration 애노테이션 한개가 추가 됐을 뿐이다.

눈치빠른 사람들은 눈치챘겠지만 junit5 또한 메타 애노테이션을 지원한다. Spring과 비슷하게 가고 있는 듯하다. 테스트를 위해 좀 더 코드를 작성해보자.

@Configuration
public class Application {

  @Bean
  HelloService helloService() {
    return () -> "hello world";
  }

  @Profile("default")
  static class TestOrder {

    @Bean
    OrderService orderService() {
      return name -> "hello " + name;
    }
  }
  @Profile("dev")
  static class DevOrder {

    @Bean
    OrderService devOrderService() {
      return name -> "hello dev " + name;
    }
  }
}

아까 위의 코드에서 클래스 2개가 추가 되었다. 하나의 클래스는 Profile이 default 일때 또 다른 한개의 클래스는 dev일 때 동작한다고 가정해보자. 그리고나서 아래와 같은 메타 어노테이션을 만들어보자.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@SpringJUnitConfig(Application.class)
@ActiveProfiles("dev")
public @interface DevTestConfig {
}

여기에는 @SpringJUnitConfig@ActiveProfiles 애노테이션 두개가 메타 어노테이션으로 정의 되어있다. 이 어노테이션은 dev의 Profile만 적용되는 애노테이션이다. 한번 그렇게 되나 테스트를 해보자.

@DevTestConfig
class DevHelloServiceTest {
  private final OrderService orderService;

  @Autowired
  DevHelloServiceTest(OrderService orderService) {
    this.orderService = orderService;
  }

  @Test
  void hello() {
    assertEquals(orderService.ordered("wonwoo"), "hello dev wonwoo");
  }
}

위에서 보다 시피 dev쪽의 bean은 "hello dev " + name 문자열로 dev라는 문자열이 포함되어 있다. 만약 dev 프로파일이 적용이 되었다면 위의 테스트 코드는 통과해야만 한다. 만약 그렇지 않고 기본 프로파일이 적용된다면 이 테스트는 실패로 돌아간다.
한번 테스트를 돌려보면 이상없이 테스트 코드가 잘 동작하는 것을 볼 수 있다.

우리는 이렇게 junut5로 spring를 테스트하는 코드를 작성해봤다. 그렇게 많이 어려운 내용은 아닌 듯 싶다. 아직 당분간은 회사에서 사용할 일은 없기에 개인적으로만 사용해야겠다.

위의 해당 소스는 여기에 있으니 한번씩 돌려보도록 하자.