프레임워크/Spring boot

Spring boot @RunWith가 어디에 ?

pine tree root 2022. 2. 5. 14:11

인프런에 스프링 부트 강의를 만들었을 당시(2018년 7월) 스프링 부트 2,0이 막 출시된 지 얼마지나지 않았을 시점이었다. 강좌를 공개하고 반년 정도 흘렀을까.. 스프링 부트 2.1이 출시되었고 스프링 부트의 철학이 그러하듯 하위호환성 보다는 편의성과 기술 흐름을 빠르게 쫓는 변화가 생겼다.

여러 변경 사항이 있지만 그 중에서도 단연 눈에 띄는 변화 중 하나는 JUnit의 버전을 4쩜대(4.x)에서 5.2로 변경한 것이다. 보통 여러 라이브러리나 프레임워크가 이렇게 메이저 버전이 바뀌는 경우 하위호환성이 깨지거나 구조상의 변경이 생기기 마련인데 JUnit도 예외는 아니었고 상당 부분 의미있는 변경이 있었다. 특히 JUnit을 확장하는 방법에도 변화가 있었다. (자세한 건 '더 자바, 테스트 강의'에서...)

 

 

4.x에서 JUnit의 테스트 러너(Runner)를 확장하는 방법중 하나로 @RunWith 애노테이션에 커스텀 테스트 러너를 설정해주는 방법이 있다. (Rule을 사용하는 방법도 제공한다.) 그래서 스프링 또는 스프링 부트로 테스트를 작성할 때 주로 @RunWith(SpringRunner.class)와 같은 코드를 보았을 것이다.

그런데 JUnit 5부터는 @RunWith가 아닌 Extension이라는 일관된 방법을 통해 테스트를 실행하는 방법을 커스터 마이징 하는 것이다. 사용법은 @RunWith와 비슷하게 @ExtendWith라는 애노테이션을 사용해서 @ExtendWith(MyExtension.class)처럼  Extension 구현체를 지정해 줄 수 있는데 @RunWith에 비해 몇가지 중요한 차이가 있다.

  • 메타 애노테이션을 지원한다.
  • 여러번 중복 사용할 수 있다.

이 중에서 메타 애노테이션으로 사용할 수 있다는 장점을 스프링 부트가 적극적으로 활용하면서 @ExtendWith(SpringExtension.class)를 생략할 수 있게 됐다.

@SpringBootTest
@AutoConfigureMockMvc
class PostControllerTest {
...
}

이 코드 어디에도 @RunWith나 @ExtendWith가 없지만 사실 이 코드는 @ExtendWith(SpringExtension.class)를 가지고 있는것과 같다. @SpringBootTest가 이미 그 코드를 가지고 있기 때문이다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
public @interface SpringBootTest {
    @AliasFor("properties")
    String[] value() default {};
...
}

이 코드는 SpringBootTest 애노테이션의 소스 코드 일부인데 보다시피, @ExtendWith(SpringExtension.class)가 이미 메타 애노테이션으로 사용됐기 때문에 @SpringBootTest를 사용하는 코드에 다시 선언하지 않아도 된다.

정리하자면, 최근 스프링 부트는 JUnit 5를 사용하기 때문에 더이상 JUnit 4에서 제공하던 @RunWith를 쓸 필요가 없고 (쓰고 싶으면 쓸 수는 있지만), @ExtendWith를 사용해야 하지만, 이미 스프링 부트가 제공하는 모든 테스트용 애노테이션에 메타 애노테이션으로 적용되어 있기 때문에 @ExtendWith(SpringExtension.class)를 생략할 수 있다.

 

출처 : https://www.whiteship.me/springboot-no-more-runwith/