토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 부트 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.

목차


7.26. Testing

스프링 부트는 애플리케이션 테스트를 도와줄 다양한 유틸리티와 어노테이션을 제공한다. 핵심 아이템들을 가지고 있는 spring-boot-test와, 테스트를 위한 자동 설정을 지원하는 spring-boot-test-autoconfigure, 이 두 가지 모듈로 테스트를 지원한다.

대부분의 개발자들은 스프링 부트 테스트 모듈과 함께 JUnit Jupiter, AssertJ, Hamcrest 등의 여러 가지 유용한 라이브러리를 함께 가져오는 spring-boot-starter-test “스타터”를 사용한다.

JUnit 4를 사용하는 테스트가 있을 때는 JUnit 5의 vintage 엔진으로 실행할 수 있다. vintage 엔진을 사용하려면 아래처럼 junit-vintage-engine 의존성을 추가해라.

<dependency>
 <groupId>org.junit.vintage</groupId>
 <artifactId>junit-vintage-engine</artifactId>
 <scope>test</scope>
 <exclusions>
     <exclusion>
         <groupId>org.hamcrest</groupId>
         <artifactId>hamcrest-core</artifactId>
     </exclusion>
 </exclusions>
</dependency>

spring-boot-starter-test에 들어있는 org.hamcrest:hamcrest를 사용하기 위해 hamcrest-core는 제외한다.

7.26.1. Test Scope Dependencies

spring-boot-starter-test “스타터”는 다음과 같은 라이브러리를 포함하고 있다 (test scope):

테스트를 작성할 때 일반적으로 많이 쓰는 라이브러리들을 모아놨다. 이런 라이브러리가 요구사항에 잘 맞지 않으면, 자체 테스트 의존성을 별도로 추가해도 된다.

7.26.2. Testing Spring Applications

의존성 주입의 큰 장점 중 하나는 코드를 단위 테스트하기가 쉬워진다는 거다. 심지어 스프링을 사용하지 않고도 new 연산자로 객체 인스턴스를 만들 수 있다. 실제 의존성 대신 mock 객체를 사용할 수도 있다.

단위 테스트를 넘어 통합 테스트를 시작해야 할 때가 자주 있다 (스프링 ApplicationContext를 사용해서). 애플리케이션을 배포하거나 다른 인프라에 연결하지 않고도 통합 테스트를 수행할 수 있으면 매우 유용할 거다.

스프링 프레임워크에는 이런 통합 테스트를 위한 전용 테스트 모듈이 따로 있다. org.springframework:spring-test 의존성을 직접 선언해도 되고, spring-boot-starter-test “스타터”를 통해 간접적으로transitively 의존성을 가져와도 된다.

전에 spring-test 모듈을 사용해본 적이 없다면 먼저 스프링 프레임워크 레퍼런스 문서에서 관련 섹션을 읽어보고 오는 게 좋다.

7.26.3. Testing Spring Boot Applications

스프링 부트 애플리케이션도 결국 스프링 ApplicationContext기 때문에, 테스트를 위해 평소 순수vanilla 스프링 컨텍스트로 설정해주는 것 외엔 특별히 해야 할 일은 없다.

외부 프로퍼티, 로깅 등, 스프링 부트의 기능들은 SpringApplication을 통해 컨텍스트를 생성할 때만 기본으로 컨텍스트에 설치된다.

스프링 부트는 스프링 부트 기능이 필요할 때 표준 spring-test @ContextConfiguration 어노테이션 대신 사용할 수 있는 @SpringBootTest 어노테이션을 제공한다. 이 어노테이션은 SpringApplication을 통해 테스트에 사용할 ApplicationContext를 생성하는 식으로 동작한다. @SpringBootTest 외에도, 좀 더 구체적인 애플리케이션의 슬라이스를 테스트할 수 있는 여러 가지 어노테이션들도 제공한다.

JUnit 4를 사용한다면 테스트에 @RunWith(SpringRunner.class)를 추가하는 것을 잊지 마라. 추가해주지 않으면 이 어노테이션들을 무시한다. JUnit 5를 사용할 때는 이와 동일한 @ExtendWith(SpringExtension.class)를 추가해주지 않아도 되는데, @SpringBootTest나 다른 @…Test 어노테이션에 이미 선언돼 있기 때문이다.

기본적으로 @SpringBootTest는 서버를 시작하지 않는다. @SpringBootTestwebEnvironment 속성을 사용하면 테스트 실행 방법을 더욱 세분화할 수 있다:

테스트가 @Transactional일 때는 기본적으로 각 테스트 메소드가 끝나면 트랜잭션을 롤백한다. 하지만 RANDOM_PORTDEFINED_PORT와 함께 쓰면 실제 서블릿 환경을 제공한다는 걸 암시하기 때문에, HTTP 클라이언트와 서버는 별도 스레드에서 실행되고, 따라서 별도 트랜잭션에서 실행된다. 이럴 땐 서버에서 시작한 트랜잭션은 롤백되지 않는다.

애플리케이션이 management 서버에 다른 포트를 사용할 때는, @SpringBootTest에서 webEnvironment = WebEnvironment.RANDOM_PORT를 사용하면 management 서버도 별도의 랜덤 포트에서 시작한다.

Detecting Web Application Type

클래스패스에 스프링 MVC가 있으면 전형적인 MVC 기반 애플리케이션 컨텍스트를 설정한다. 스프링 웹플럭스만 있을 땐 이를 감지해서, 그대신 웹플럭스 기반 애플리케이션 컨텍스트를 설정한다.

둘 다 있을 때는 스프링 MVC를 우선시한다. 이럴 때 리액티브 웹 애플리케이션을 테스트하고 싶으면, 반드시 spring.main.web-application-type 프로퍼티를 설정해야 한다:

@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {

    // ...

}

Detecting Test Configuration

스프링 테스트 프레임워크를 많이 사용해봤다면, @ContextConfiguration(classes=…)를 사용해 로드할 스프링 @Configuration을 지정하는 것에 익숙할 거다. 아니면 테스트 코드 안에 @Configuration 클래스를 중첩해서 자주 사용했을 거다.

스프링 부트 애플리케이션을 테스트할 때는 보통은 그럴 필요가 없다. 설정을 명시해주지 않았을 땐 언제나 스프링 부트의 @*Test 어노테이션이 주요 설정을 자동으로 검색한다.

이 검색 알고리즘에선 @SpringBootApplication이나 @SpringBootConfiguration 어노테이션이 달린 클래스를 찾을 때까지 테스트가 포함된 패키지를 탐색한다. 베스트 프랙티스에 맞게 코드 구조를 잡으면 보통은 메인 설정을 찾는다.

테스트 어노테이션으로 좀 더 세세한 애플리케이션의 슬라이스를 테스트한다면, 메인 메소드를 가진 애플리케이션 클래스엔 특정 영역에 귀속된 설정을 추가하지 않는 것이 좋다.

@SpringBootApplication에서는 사용하는 컴포넌트 스캔 설정에 예외 필터를 정의하며, 이 필터를 사용해서 슬라이싱이 예상대로 동작하는지 확인한다. @SpringBootApplication 어노테이션을 선언한 클래스에 @ComponentScan 명령을 명시하게되면, 이 필터를 비활성화한다는 점을 유의해라. 슬라이싱을 사용하려면 필터를 다시 정의해야 한다.

주요 설정을 커스텀하고 싶으면 @TestConfiguration 클래스를 중첩해서 사용하면 된다. @Configuration 클래스를 중첩하면 애플리케이션의 주요 설정 대신 사용하지만, 이와는 달리 @TestConfiguration 클래스를 중첩하면 애플리케이션의 주요 설정 위에 추가된다.

스프링의 테스트 프레임워크는 애플리케이션 컨텍스트를 캐시해서 여러 테스트에 사용한다. 따라서 여러 테스트에서 같은 설정을 공유하기만 하면 (설정을 검색하는 방법과는 상관없이), 많은 시간이 소요되곤 하는 컨텍스트 로드 프로세스는 딱 한 번만 일어난다.

Excluding Test Configuration

컴포넌트 스캔을 이용하고 있다면 (ex. @SpringBootApplication이나 @ComponentScan을 사용한다면), 특정 테스트용으로만 생성한 최상위 설정 클래스를 의도치않게 모든 곳에서 사용하고 있는 것을 발견할 수도 있다.

앞에서도 살펴봤지만 주요 설정을 커스텀할 때는 @TestConfiguration을 테스트의 내부 클래스에 사용할 수 있다. 이 @TestConfiguration을 최상위 클래스에 두게 되면, 스캔 중에 @TestConfiguration 클래스들은 src/test/java에 있더라도 선택하지 않아야 된다는 걸 나타낸다. @TestConfiguration을 선언한 클래스를 사용하려면 다음 예제처럼 필요한 위치에 직접 임포트하면 된다:

@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {

    @Test
    void exampleTest() {
        // ...
    }

}

@ComponentScan을 직접 사용할 때는 (다시 말해 @SpringBootApplication을 통하지 않으면), TypeExcludeFilter도 함께 등록해야 한다. 자세한 내용은 Javadoc을 참고해라.

Using Application Arguments

애플리케이션에서 인자를 받고 있다면, @SpringBootTestargs 속성을 사용해서 인자를 주입시킬 수 있다:

@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {

    @Test
    void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
        assertThat(args.getOptionNames()).containsOnly("app.test");
        assertThat(args.getOptionValues("app.test")).containsOnly("one");
    }

}

Testing with a mock environment

기본적으로 @SpringBootTest는 서버를 시작하지 않는다. 이 mock 환경으로 테스트해보고 싶은 웹 엔드포인트가 있을 땐 아래 예제처럼 MockMvc를 별도로 추가하면 된다:

@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {

    @Test
    void exampleTest(@Autowired MockMvc mvc) throws Exception {
        mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
    }

}

모든 걸 다 갖춘 ApplicationContext를 시작하기 보단 웹 레이어에만 집중하고 싶으면 이대신 @WebMvcTest 사용을 검토해봐라.

아니면 다음 예제처럼 WebTestClient를 설정해도 된다:

@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}

모킹한 환경을 통해 테스트를 수행하면 보통 서블릿 컨테이너를 통째로 실행할 때보다 빠르다. 하지만 모킹은 스프링 MVC 계층에서 이뤄지기 때문에, 저수준에 있는 서블릿 컨테이너 동작에 의존하는 코드는 MockMvc로는 바로 테스트할 수 없다.

예를 들어, 스프링 부트는 서블릿 컨테이너에서 제공하는 “에러 페이지”를 기반으로 에러를 처리한다. 즉, MVC 계층에서 예상대로 예외를 던지고 처리하는지는 테스트할 수 있지만, 특정 커스텀 에러 페이지가 렌더링되는지는 직접 테스트할 수 없다는 뜻이다. 이렇게 저수준에 있는 관심사를 테스트해야 할 때는 다음 섹션에 설명하는 방법대로 서버 전체를 실행시키면 된다.

Testing with a running server

전체를 다 실행하는 서버를 시작해야 할 때는 랜덤 포트를 사용하는 게 좋다. @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)를 사용하면 테스트를 실행할 때마다 사용 가능한 포트를 무작위로 고른다.

@LocalServerPort 어노테이션을 사용하면 테스트에 실제로 사용한 포트를 주입할 수 있다. 기동시킨 서버에 REST 호출을 날려야 하는 테스트에선 편의상 아래 예제처럼 WebTestClient를 추가로 @Autowire해도 된다. WebTestClient는 실행 중인 서버에 대한 상대적인 링크를 리졸브하며, 응답 검증을 위한 전용 API를 함께 제공한다:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {

    @Test
    void exampleTest(@Autowired WebTestClient webClient) {
        webClient
            .get().uri("/")
            .exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Hello World");
    }

}

이 구조에서는 클래스패스에 spring-webflux가 있어야 한다. 웹플럭스를 추가할 수 없거나, 추가할 생각이 없다면, 스프링 부트는 TestRestTemplate도 제공하고 있다:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {

    @Test
    void exampleTest(@Autowired TestRestTemplate restTemplate) {
        String body = restTemplate.getForObject("/", String.class);
        assertThat(body).isEqualTo("Hello World");
    }

}

Customizing WebTestClient

WebTestClient 빈을 커스텀하고 싶으면 WebTestClientBuilderCustomizer 빈을 설정해라. Customizer 빈들은 WebTestClient를 생성할 때 사용하는 WebTestClient.Builder를 넘겨서 호출한다.

Using JMX

테스트 컨텍스트 프레임워크는 컨텍스트를 캐시하기 때문에, JMX는 동일한 컴포넌트들을 같은 도메인에 등록하지 않도록 기본적으로 비활성화한다. 이런 테스트에서 MBeanServer에 접근해야 한다면 dirty로 마킹하는 것도 고려해봐라:

@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {

    @Autowired
    private MBeanServer mBeanServer;

    @Test
    void exampleTest() throws MalformedObjectNameException {
        assertThat(this.mBeanServer.getDomains()).contains("java.lang");
        // ...
    }

}

Using Metrics

클래스패스에 어떤 게 들어 있더라도, @SpringBootTest를 사용할 때는 meter 레지스트리는 인메모리를 사용하는 레지스트리 외엔 자동으로 설정되지 않는다.

통합 테스트에서 메트릭을 다른 백엔드로 내보내고 싶으면 @AutoConfigureMetrics 어노테이션을 추가해라.

Mocking and Spying Beans

테스트를 실행할 때는 애플리케이션 컨텍스트 내에 있는 특정 컴포넌트를 모킹해야 하는 때가 종종 있다. 예를 들어, 개발 중에는 사용할 수 없는 어떤 리모트 서비스를 이용하는 파사드가 있을 수 있다. 실제 환경에서 트리거하긴 어려운 실패 상황을 시뮬레이션하고 싶을 때도 모킹이 유용할 거다.

스프링 부트에는 ApplicationContext 안에 있는 빈을 위한 Mockito mock을 정의할 수 있는 @MockBean 어노테이션이 들어있다. 이 어노테이션을 사용하면 빈을 새로 추가하거나, 기존에 있던 빈 정의 하나를 대체할 수 있다. 테스트 클래스에 어노테이션을 바로 사용해도 되고, 테스트 안에 있는 필드나, @Configuration 클래스 및 필드에 사용해도 된다. 필드에 사용하면 mock 인스턴스를 만들어 주입까지 해준다. Mock 빈은 각 테스트 메소드를 실행한 후에 자동으로 리셋된다.

테스트에 스프링 부트의 테스트 어노테이션 중 하나를 사용하고 있다면 (ex. @SpringBootTest), 이 기능은 자동으로 활성화된다. 이 기능을 다른 구조에서 사용하려면 다음 예제처럼 반드시 리스너를 명시적으로 추가해줘야 한다:

@ContextConfiguration(classes = MyConfig.class)
@TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class })
class MyTests {

    // ...

}

아래 예제에선 기존 RemoteService 빈을 mock 구현체로 대체하고 있다:

@SpringBootTest
class MyTests {

    @Autowired
    private Reverser reverser;

    @MockBean
    private RemoteService remoteService;

    @Test
    void exampleTest() {
        given(this.remoteService.getValue()).willReturn("spring");
        String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
        assertThat(reverse).isEqualTo("gnirps");
    }

}

@MockBean으로는 애플리케이션 컨텍스트를 리프레시하는 중에 실행되는 빈 동작은 모킹할 수 없다. 테스트가 실행되는 시점에는 애플리케이션 컨텍스트 리프레시가 완료돼 있을 거고, 모킹한 동작을 설정하기에는 너무 늦는다. 이런 상황에서는 @Bean 메소드를 통해 mock을 생성하고 설정하는 게 좋다.

추가로, @SpyBean을 사용하면 기존 빈을 Mockito spy로 래핑할 수 있다. 자세한 내용은 Javadoc을 참고해라.

범위가 한정된scoped 빈에다가 프록시를 생성하는 등, CGLib 프록시를 만들게 되면 프록시하는 메소드를 final으로 선언한다. 이렇게 되면 디폴트 설정에선 final 메소드에 mock이나 spy를 적용할 수 없기 때문에, Mockito는 더 이상 제대로 동작하지 않는다. 이런 빈에 mock이나 spy를 적용하려면 애플리케이션의 테스트 의존성에 org.mockito:mockito-inline을 추가해서 Mockito에서 inline mock maker를 사용하도록 설정해라. inline mock maker는 Mockito가 final 메소드에 mock과 spy를 적용할 수 있게 해준다.

스프링의 테스트 프레임워크는 여러 테스트를 실행하면서 애플리케이션 컨텍스트를 캐시하고, 같은 설정을 공유하는 테스트에선 컨텍스트를 재사용하지만, @MockBean이나 @SpyBean을 사용하면 캐시 키가 달라질 수 있어서 컨텍스트 갯수가 늘어날 가능성이 크다.

@Cacheable 메소드에서 이름으로 파라미터를 참조하고 있는 빈에 @SpyBean으로 spy를 적용하는 경우, 반드시 애플리케이션을 -parameters로 컴파일해야 한다. 이렇게 해야 빈에 spy를 적용한 다음 캐시 인프라에서 파라미터 이름을 활용할 수 있다.

스프링에서 프록시로 감싸는 빈에 @SpyBean을 통해 spy를 적용할 때는, 상황에 따라 스프링의 프록시를 제거해야 할 수도 있다. 예를 들어 given이나 when을 사용해서 기대값을 설정할 때가 그렇다. 이럴 땐 AopTestUtils.getTargetObject(yourProxiedSpy)를 사용해라.

Auto-configured Tests

스프링 부트의 자동 설정 시스템은 대부분의 애플리케이션에 잘 맞지만, 간혹 테스트에 사용하기엔 너무 과할 때도 있다. 애플리케이션의 “조각slice“을 테스트하는 데 필요한 일부 설정만 로드할 수 있으면 종종 유용할 거다. 예를 들어, 스프링 MVC 컨트롤러가 URL을 제대로 매핑하는지 테스트하고 싶은데 이 테스트에선 데이터베이스 호출은 제외시키고 싶을 수도 있고, JPA 엔티티를 테스트하고 싶지만 웹 레이어에는 관심이 없을 수도 있다.

spring-boot-test-autoconfigure 모듈에는 이런 “슬라이스”들을 자동으로 설정할 때 활용할 수 있는 여러 가지 어노테이션들이 들어있다. 이 어노테이션들은 전부 유사한 방식으로 동작하며, ApplicationContext를 로드하는 @…Test 어노테이션과, 자동 설정을 커스텀할 수 있는 @AutoConfigure… 어노테이션을 하나 이상 제공한다.

각 슬라이스는 컴포넌트 스캔을 적당한 컴포넌트들로 제한하며, 로드하는 자동 설정 클래스도 매우 한정적이다. 그 중에서도 하나를 제외해야 한다면, @…Test 어노테이션 대부분은 excludeAutoConfiguration 속성을 제공한다. 아니면 @ImportAutoConfiguration#exclude를 사용해도 된다.

테스트 하나에서 @…Test 어노테이션을 여러 개 사용하는 식으로 “슬라이스”를 여러개 추가하는 건 지원하지 않는다. “슬라이스”가 여러 개 필요한 경우 @…Test 어노테이션 중에서 하나를 고르고, 다른 “슬라이스”는 @AutoConfigure… 어노테이션으로 직접 추가해라.

@AutoConfigure… 어노테이션은 표준 @SpringBootTest 어노테이션과도 함께 사용할 수 있다. 애플리케이션을 “슬라이싱”하는 것에는 관심 없지만, 테스트 빈을 자동 설정하고 싶다면 이 조합을 사용하면 된다.

Auto-configured JSON Tests

객체가 예상대로 JSON으로 직렬화되고 역직렬화되는지를 테스트하려면 @JsonTest 어노테이션을 사용하면 된다. @JsonTest는 지원하는 JSON 매퍼를 찾아 자동 설정한다. 지원하는 라이브러리는 다음과 같다:

@JsonTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

자동 설정되는 헬퍼를 따로 설정하고 싶을 땐 @AutoConfigureJsonTesters 어노테이션을 사용하면 된다.

스프링 부트에는 JSONAssert와 JsonPath 라이브러리와 함께 동작하는 AssertJ 기반 헬퍼가 들어있는데, 이 헬퍼로 JSON을 기대한 대로 만들 수 있는지 검증할 수 있다. JacksonTester, GsonTester, JsonbTester, BasicJsonTester 클래스는 각각 Jackson, Gson, Jsonb, Strings에 이용할 수 있다. @JsonTest를 사용하면 모든 헬퍼를 테스트 클래스의 필드로 @Autowired할 수 있다. 다음은 Jackson을 위한 테스트 클래스 예시다:

@JsonTest
class MyJsonTests {

    @Autowired
    private JacksonTester<VehicleDetails> json;

    @Test
    void serialize() throws Exception {
        VehicleDetails details = new VehicleDetails("Honda", "Civic");
        // Assert against a `.json` file in the same package as the test
        assertThat(this.json.write(details)).isEqualToJson("expected.json");
        // Or use JSON path based assertions
        assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
        assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
    }

    @Test
    void deserialize() throws Exception {
        String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
        assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
        assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
    }

}

JSON 헬퍼 클래스들은 표준 단위 테스트에도 직접 사용할 수 있다. @JsonTest를 사용하지 않을 때는 @Before 메소드에서 헬퍼의 initFields 메소드를 호출해라.

스프링 부트의 AssertJ 기반 헬퍼를 통해 주어진 JSON path에서 숫자 값을 검증할 때는, 타입에 따라 isEqualTo를 쓰지 못할 수도 있다. 이럴 땐 isEqualTo 대신 AssertJ의 satisfies를 사용해서 값이 주어진 조건과 일치하는지 검증할 수 있다. 예를 들어 아래 예제에선 실제 숫자가 오프셋 0.01 내로 0.15에 근접한 float 값인지를 검증한다.

@Test
void someTest() throws Exception {
    SomeObject value = new SomeObject(0.152f);
    assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
            .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}

Auto-configured Spring MVC Tests

스프링 MVC 컨트롤러가 예상대로 동작하는지를 테스트하려면 @WebMvcTest 어노테이션을 사용해라. @WebMvcTest는 스프링 MVC 인프라를 자동 설정하고, 스캔하는 빈을 @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, HandlerMethodArgumentResolver로 제한한다. @WebMvcTest 어노테이션을 사용할 땐 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@WebMvcTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

Jackson Module같이 다른 컴포넌트들도 등록해야 한다면, 테스트에 @Import를 사용하면 별도 설정 클래스를 임포트할 수 있다.

@WebMvcTest는 보통 단일 컨트롤러로 제한하며, @MockBean과 함께 사용해서 필요한 조력 컴포넌트의 mock 구현체를 제공한다.

@WebMvcTestMockMvc도 자동 설정한다. Mock MVC를 사용하면 HTTP 서버를 통째로 기동하지 않고도 MVC 컨트롤러를 빠르고 확실하게 테스트해볼 수 있다.

@WebMvcTest가 아닐 때도 (@SpringBootTest 등) @AutoConfigureMockMvc 어노테이션을 달면 MockMvc를 자동 설정할 수 있다. 다음은 MockMvc를 사용하는 예시다:

@WebMvcTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot"))
            .willReturn(new VehicleDetails("Honda", "Civic"));
        this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andExpect(content().string("Honda Civic"));
    }

}

자동 설정되는 요소들을 따로 설정하고 싶을 땐 (예를 들어 서블릿 필터를 적용해야 한다면) @AutoConfigureMockMvc 어노테이션의 속성을 이용하면 된다.

HtmlUnit이나 Selenium을 사용한다면, 자동 설정은 HtmlUnit WebClient 빈이나 Selenium WebDriver 빈도 함께 제공한다. 다음 예제에선 HtmlUnit을 사용한다:

@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {

    @Autowired
    private WebClient webClient;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
        HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
        assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
    }

}

기본적으로 스프링 부트는 각 테스트를 완료하고 나면 드라이버를 종료하고 새 인스턴스를 주입할 수 있도록 WebDriver 빈을 특별한 “스코프”에 넣는다. 이 동작이 싫다면 WebDriver @Bean을 정의해서 @Scope("singleton")을 추가하면 된다.

스프링 부트에서 생성한 webDriver 스코프는 같은 이름을 가진 사용자 정의 스코프를 대체한다. @WebMvcTest를 사용할 때 자체 webDriver 스코프를 정의하면 동작이 멈출 수도 있다.

클래스패스에 스프링 시큐리티가 있다면 @WebMvcTestWebSecurityConfigurer 빈도 함께 스캔한다. 이럴 때 테스트에서 완전히 보안을 비활성화하기 보단, 스프링 시큐리티의 테스트 지원을 이용할 수 있다. 어떻게 스프링 시큐리티의 MockMvc 지원을 이용할 수 있는지는 how-to 섹션 테스트에 스프링 시큐리티 활용하기에서 확인할 수 있다.

스프링 MVC 테스트를 작성하는 것만으로는 충분하지 않을 때가 있다. 스프링 부트는 실제 서버를 통한 완전한 end-to-end 테스트 실행도 도와준다.

Auto-configured Spring WebFlux Tests

스프링 웹플럭스 컨트롤러가 예상대로 동작하는지를 테스트하려면 @WebFluxTest 어노테이션을 사용해라. @WebFluxTest는 스프링 웹플럭스 인프라를 자동 설정하고, 스캔하는 빈을 @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, WebFilter, WebFluxConfigurer로 제한한다. @WebFluxTest 어노테이션을 사용할 땐 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@WebFluxTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

Jackson Module같이 다른 컴포넌트들도 등록해야 한다면, 테스트에 @Import를 사용하면 별도 설정 클래스를 임포트할 수 있다.

@WebFluxTest는 보통 단일 컨트롤러로 제한하며, @MockBean과 함께 사용해서 필요한 조력 컴포넌트의 mock 구현체를 제공한다.

@WebFluxTestWebTestClient도 자동 설정한다. WebTestClient를 사용하면 HTTP 서버를 통째로 기동하지 않고도 웹플럭스 컨트롤러를 빠르고 확실하게 테스트해볼 수 있다.

@WebFluxTest가 아닐 때도 (@SpringBootTest 등) @AutoConfigureWebTestClient 어노테이션을 달면 WebTestClient를 자동 설정할 수 있다. 아래 예제에선 @WebFluxTestWebTestClient를 함께 사용하는 클래스를 보여주고 있다:

@WebFluxTest(UserVehicleController.class)
class MyControllerTests {

    @Autowired
    private WebTestClient webClient;

    @MockBean
    private UserVehicleService userVehicleService;

    @Test
    void testExample() throws Exception {
        given(this.userVehicleService.getVehicleDetails("sboot"))
            .willReturn(new VehicleDetails("Honda", "Civic"));
        this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
            .expectStatus().isOk()
            .expectBody(String.class).isEqualTo("Honda Civic");
    }

}

mock 웹 애플리케이션에 WebTestClient를 사용하는 건 현재로썬 웹플럭스에서만 동작하기 때문에, 이 구조는 웹플럭스 애플리케이션에서만 지원한다.

@WebFluxTest는 함수형 웹 프레임워크를 통해 등록한 라우트는 감지하지 못한다. 컨텍스트에 있는 RouterFunction 빈을 테스트하려면 @Import를 통해 직접 RouterFunction을 임포트하거나 @SpringBootTest를 사용하는 게 좋다.

@WebFluxTestSecurityWebFilterChain 타입 @Bean을 통해 등록한 커스텀 시큐리티 설정은 감지하지 못한다. 테스트에서 사용하려면 @Import를 통해 빈을 등록하고 있는 설정을 임포트하거나 @SpringBootTest를 사용해야 할 거다.

스프링 웹플럭스 테스트를 작성하는 것만으로는 충분하지 않을 때가 있다. 스프링 부트는 실제 서버를 통한 완전한 end-to-end 테스트 실행도 도와준다.

Auto-configured Data Cassandra Tests

Cassandra 애플리케이션을 테스트할 땐 @DataCassandraTest를 사용할 수 있다. 이 어노테이션은 기본적으로 CassandraTemplate을 설정하고, @Table 클래스를 스캔하며, Spring Data Cassandra 레포지토리를 구성한다. @DataCassandraTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다. (스프링 부트에서 Cassandra를 사용하는 방법은 이 챕터 앞에서 나온 “Cassandra“를 참고해라.)

@DataCassandraTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

아래 예시는 스프링 부트에서 Cassandra 테스트를 이용하는 전형적인 구성을 보여준다:

@DataCassandraTest
class MyDataCassandraTests {

    @Autowired
    private SomeRepository repository;

}

Auto-configured Data JPA Tests

JPA 애플리케이션을 테스트할 땐 @DataJpaTest 어노테이션을 사용할 수 있다. 이 어노테이션은 기본적으로 @Entity 클래스를 스캔하고 Spring Data JPA 레포지토리를 구성한다. 클래스패스에 임베디드 데이터베이스가 있으면 임베디드 DB도 구성한다. spring.jpa.show-sql 프로퍼티는 기본적으로 true로 설정돼서 SQL 쿼리를 로깅한다. 쿼리 로깅은 어노테이션에 있는 showSql() 속성으로 비활성화할 수 있다.

@DataJpaTest 어노테이션을 사용할 땐 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@DataJpaTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

기본적으로 data JPA 테스트는 트랜잭션을 사용하며, 각 테스트가 끝날 때 롤백을 수행한다. 자세한 내용은 스프링 프레임워크 레퍼런스 문서에서 관련 섹션을 참고해라. 이 동작이 싫다면 다음과 같이 테스트나, 아니면 클래스에 통째로 트랜잭션 관리를 비활성화할 수 있다:

@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyNonTransactionalTests {

    // ...

}

Data JPA 테스트에선 TestEntityManager 빈도 주입할 수 있는데, 이 빈은 표준 JPA EntityManager 대신 사용할 수 있는 특별한 테스트 전용 빈이다. TestEntityManager@DataJpaTest 인스턴스 밖에서 사용하고 싶다면 @AutoConfigureTestEntityManager 어노테이션을 사용해도 된다. 필요하면 JdbcTemplate도 사용할 수 있다. @DataJpaTest 어노테이션을 사용하는 예제는 아래 있다:

@DataJpaTest
class MyRepositoryTests {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    void testExample() throws Exception {
        this.entityManager.persist(new User("sboot", "1234"));
        User user = this.repository.findByUsername("sboot");
        assertThat(user.getUsername()).isEqualTo("sboot");
        assertThat(user.getEmployeeNumber()).isEqualTo("1234");
    }

}

인메모리 임베디드 데이터베이스는 빠르고 설치가 필요 없기 때문에 보통 테스트하기에 좋다. 하지만 실제 데이터베이스로 테스트를 실행하고 싶을 땐, 다음 예제처럼 @AutoConfigureTestDatabase 어노테이션을 사용하면 된다:

@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MyRepositoryTests {

    // ...

}

Auto-configured JDBC Tests

@JdbcTest@DataJpaTest와 유사하지만, DataSource만 필요하고 Spring Data JDBC를 사용하지 않는 테스트에 사용한다. 기본적으로 인메모리 임베디드 데이터베이스와 JdbcTemplate을 설정한다. @JdbcTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@JdbcTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

기본적으로 JDBC 테스트는 트랜잭션을 사용하며, 각 테스트가 끝날 때 롤백을 수행한다. 자세한 내용은 스프링 프레임워크 레퍼런스 문서에서 관련 섹션을 참고해라. 이 동작이 싫다면 다음과 같이 테스트나, 아니면 클래스에 통째로 트랜잭션 관리를 비활성화할 수 있다:

@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyTransactionalTests {

}

실제 데이터베이스에서 테스트를 실행하고 싶다면 DataJpaTest에서와 동일한 방식으로 @AutoConfigureTestDatabase 어노테이션을 사용하면 된다. (“Auto-configured Data JPA Tests” 참고.)

Auto-configured Data JDBC Tests

@DataJdbcTest@JdbcTest와 유사하지만, Spring Data JDBC를 사용하는 테스트에 사용한다. 기본적으로 인메모리 임베디드 데이터베이스와 JdbcTemplate, Spring Data JDBC 레포지토리를 설정한다. @DataJdbcTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@DataJdbcTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

기본적으로 Data JDBC 테스트는 트랜잭션을 사용하며, 각 테스트가 끝날 때 롤백을 수행한다. 자세한 내용은 스프링 프레임워크 레퍼런스 문서에서 관련 섹션을 참고해라. 이 동작이 싫다면 JDBC 예제에서 보여준대로 테스트나, 아니면 클래스에 통째로 트랜잭션 관리를 비활성화할 수 있다:

실제 데이터베이스에서 테스트를 실행하고 싶다면 DataJpaTest에서와 동일한 방식으로 @AutoConfigureTestDatabase 어노테이션을 사용하면 된다. (“Auto-configured Data JPA Tests” 참고.)

Auto-configured jOOQ Tests

jOOQ 관련 테스트를 진행할 땐 @JooqTest@JdbcTest와 비슷한 방식으로 사용하면 된다. jOOQ는 데이터베이스 스키마에 해당하는, 자바 기반 스키마에 크게 의존하기 때문에 기존 DataSource를 그대로 사용한다. 인메모리 데이터베이스로 교체하려면 @AutoConfigureTestDatabase를 통해 해당 설정을 재정의할 수 있다. (스프링 부트에서 jOOQ를 사용하는 방법은 이 챕터 앞에 있는 “jOOQ 사용하기“를 참고해라.) @JooqTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@JooqTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

@JooqTestDSLContext를 설정한다. 다음은 @JooqTest 어노테이션을 사용하는 예시다:

@JooqTest
class MyJooqTests {

    @Autowired
    private DSLContext dslContext;

    // ...

}

기본적으로 JOOQ 테스트는 트랜잭션을 사용하며, 각 테스트가 끝날 때 롤백을 수행한다. 이 동작이 싫다면 JDBC 예제에서 보여준대로 테스트나, 아니면 클래스에 통째로 트랜잭션 관리를 비활성화할 수 있다:

Auto-configured Data MongoDB Tests

MongoDB 애플리케이션을 테스트할 땐 @DataMongoTest를 사용할 수 있다. 이 어노테이션은 기본적으로 인메모리 임베디드 MongoDB를 구성하고 (있으면), MongoTemplate을 설정하고, @Document 클래스들을 스캔하고, Spring Data MongoDB 레포지토리를 구성한다. @DataMongoTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다. (스프링 부트에서 MongoDB를 사용하는 방법은 이 챕터 앞에 있는 “MongoDB“를 참고해라.)

@DataMongoTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

아래 클래스는 @DataMongoTest 어노테이션을 사용하는 모습을 보여준다:

@DataMongoTest
class MyDataMongoDbTests {

    @Autowired
    private MongoTemplate mongoTemplate;

    // ...

}

인메모리 임베디드 MongoDB는 빠르고 개발자 설치가 필요 없기 때문에 보통 테스트하기에 좋다. 하지만 실제 MongoDB 서버로 테스트를 실행하고 싶을 땐, 아래 예제처럼 임베디드 MongoDB 자동 설정을 제외시켜야 한다:

@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {

    // ...

}

Auto-configured Data Neo4j Tests

Neo4j 애플리케이션을 테스트할 땐 @DataNeo4jTest를 사용할 수 있다. 이 어노테이션은 기본적으로 @Node 클래스들을 스캔하고, Spring Data Neo4j 레포지토리를 구성한다. @DataNeo4jTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다. (스프링 부트에서 Neo4J를 사용하는 방법은 이 챕터 앞에 있는 “Neo4j“를 참고해라.)

@DataNeo4jTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

아래 예시는 스프링 부트에서 Neo4J 테스트를 이용하는 전형적인 구성을 보여준다:

@DataNeo4jTest
class MyDataNeo4jTests {

    @Autowired
    private SomeRepository repository;

    // ...

}

기본적으로 Data Neo4j 테스트는 트랜잭션을 사용하며, 각 테스트가 끝날 때 롤백을 수행한다. 자세한 내용은 스프링 프레임워크 레퍼런스 문서에서 관련 섹션을 참고해라. 이 동작이 싫다면 테스트나, 아니면 아래처럼 클래스에 통째로 트랜잭션 관리를 비활성화할 수 있다:

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {

}

리액티브로 접근할 때는 트랜잭션 테스트를 지원하지 않는다. 리액티브 스타일을 사용하고 있다면 @DataNeo4jTest 테스트를 바로 위에서 설명하는 대로 설정해야 한다.

Auto-configured Data Redis Tests

Redis 애플리케이션을 테스트할 땐 @DataRedisTest를 사용할 수 있다. 이 어노테이션은 기본적으로 @RedisHash 클래스들을 스캔하고, Spring Data Redis 레포지토리를 구성한다. @DataRedisTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다. (스프링 부트에서 Redis를 사용하는 방법은 이 챕터 앞에 있는 “Redis“를 참고해라.)

@DataRedisTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

다음은 @DataRedisTest 어노테이션을 사용하는 예시다:

@DataRedisTest
class MyDataRedisTests {

    @Autowired
    private SomeRepository repository;

    // ...

}

Auto-configured Data LDAP Tests

LDAP 애플리케이션을 테스트할 땐 @DataLdapTest를 사용할 수 있다. 이 어노테이션은 기본적으로 인메모리 임베디드 LDAP을 구성하고 (있으면), LdapTemplate을 설정하고, @Entry 클래스들을 스캔하고, Spring Data LDAP 레포지토리를 구성한다. @DataLdapTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다. (스프링 부트에서 LDAP을 사용하는 방법은 이 챕터 앞에 있는 “LDAP“을 참고해라.)

@DataLdapTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

다음은 @DataLdapTest 어노테이션을 사용하는 예시다:

@DataLdapTest
class MyDataLdapTests {

    @Autowired
    private LdapTemplate ldapTemplate;

    // ...

}

인메모리 임베디드 LDAP은 빠르고 개발자 설치가 필요 없기 때문에 보통 테스트하기에 좋다. 하지만 실제 LDAP 서버로 테스트를 실행하고 싶을 땐, 아래 예제처럼 임베디드 LDAP 자동 설정을 제외시켜야 한다:

@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {

    // ...

}

Auto-configured REST Clients

REST 클라이언트를 테스트할 땐 @RestClientTest를 사용할 수 있다. 이 어노테이션은 기본적으로 Jackson, GSON, Jsonb 지원을 자동 설정하고, RestTemplateBuilder를 설정하고, MockRestServiceServer 지원을 위한 기능들을 추가한다. @RestClientTest 어노테이션을 사용할 때는 전형적인 @Component@ConfigurationProperties 빈은 스캔하지 않는다. @ConfigurationProperties 빈을 포함시키려면 @EnableConfigurationProperties를 사용하면 된다.

@RestClientTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

테스트하려는 빈은 다음 예제처럼 @RestClientTestvalue 또는 components 속성을 사용해서 지정해줘야 한다:

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

    @Test
    void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() throws Exception {
        this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
        String greeting = this.service.callRestService();
        assertThat(greeting).isEqualTo("hello");
    }

}

Auto-configured Spring REST Docs Tests

Mock MVC나, REST Assured, WebTestClient를 사용하는 테스트에선 @AutoConfigureRestDocs 어노테이션을 통해 스프링 REST Docs를 사용할 수 있다. Spring REST Docs에선 JUnit 익스텐션이 없어도 된다.

@AutoConfigureRestDocs로는 디폴트 출력 디렉토리(메이븐을 사용한다면 target/generated-snippets, 또는 그래들에선 build/generated-snippets)를 재정의할 수 있다. 더불어 URI를 문서화할 때 사용할 호스트, 스킴, 포트도 설정할 수 있다.

Auto-configured Spring REST Docs Tests with Mock MVC

@AutoConfigureRestDocs는 서블릿 기반 웹 애플리케이션을 테스트할 땐 MockMvc 빈을 커스텀해서 Spring REST Docs를 사용하도록 만든다. MockMvc는 다음 예제처럼 @Autowired를 사용해 테스트에 주입하고 활용할 수 있다. 평상시 Mock MVC와 Spring REST Docs를 사용할 때와 동일하다:

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

    @Autowired
    private MockMvc mvc;

    @Test
    void listUsers() throws Exception {
        this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
            .andExpect(status().isOk())
            .andDo(document("list-users"));
    }

}

Spring REST Docs 설정을 @AutoConfigureRestDocs 속성으로 제공하는 것 이상으로 더 제어하고 싶으면, 아래 예제처럼 RestDocsMockMvcConfigurationCustomizer 빈을 사용하면 된다:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {

    @Override
    public void customize(MockMvcRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}

Spring REST Docs에서 지원하는 대로 출력 디렉토리에 파라미터를 활용하고 싶다면 RestDocumentationResultHandler 빈을 생성하면 된다. 자동 설정에선 이 Result 핸들러를 넘겨 alwaysDo를 호출하므로, MockMvc를 호출할 때마다 자동으로 디폴트 스니펫을 생성한다. 다음은 RestDocumentationResultHandler를 정의하는 예시다:

@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {

    @Bean
    public RestDocumentationResultHandler restDocumentation() {
        return MockMvcRestDocumentation.document("{method-name}");
    }

}

Auto-configured Spring REST Docs Tests with WebTestClient

@AutoConfigureRestDocsWebTestClient와 함께 사용해서 리액티브 웹 애플리케이션을 테스트할 수도 있다. WebTestClient는 다음 예제처럼 @Autowired를 사용해 테스트에 주입하고 활용할 수 있다. 평상시 @WebFluxTest와 Spring REST Docs를 사용할 때와 동일하다:

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void listUsers() {
        this.webTestClient
            .get().uri("/")
        .exchange()
        .expectStatus()
            .isOk()
        .expectBody()
            .consumeWith(document("list-users"));
    }

}

Spring REST Docs 설정을 @AutoConfigureRestDocs 속성으로 제공하는 것 이상으로 더 제어하고 싶으면, 아래 예제처럼 RestDocsWebTestClientConfigurationCustomizer 빈을 사용하면 된다:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {

    @Override
    public void customize(WebTestClientRestDocumentationConfigurer configurer) {
        configurer.snippets().withEncoding("UTF-8");
    }

}

Auto-configured Spring REST Docs Tests with REST Assured

@AutoConfigureRestDocsRequestSpecification을 미리 만들어 Spring REST Docs를 사용하도록 미리 설정해두기 때문에 RequestSpecification도 테스트에 활용할 수 있다. RequestSpecification은 다음 예제처럼 @Autowired를 사용해 테스트에 주입하고 활용할 수 있다. 평상시 REST Assured와 Spring REST Docs를 사용할 때와 동일하다:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

    @Test
    void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
        given(documentationSpec)
            .filter(document("list-users"))
        .when()
            .port(port)
            .get("/")
        .then().assertThat()
            .statusCode(is(200));
    }

}

Spring REST Docs 설정을 @AutoConfigureRestDocs 속성으로 제공하는 것 이상으로 더 제어하고 싶으면, 아래 예제처럼 RestDocsRestAssuredConfigurationCustomizer 빈을 사용하면 된다:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

    @Override
    public void customize(RestAssuredRestDocumentationConfigurer configurer) {
        configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
    }

}

Auto-configured Spring Web Services Tests

Spring Web Services 프로젝트를 통해 웹 서비스를 호출하는 애플리케이션을 테스트할 땐 @WebServiceClientTest를 사용할 수 있다. 이 어노테이션은 기본적으로 mock WebServiceServer 빈을 구성하고, WebServiceTemplateBuilder를 자동으로 커스텀해준다. (스프링 부트에서 웹 서비스를 사용하는 방법은 이 챕터 앞에 있는 “웹 서비스“를 참고해라.)

@WebServiceClientTest로 활성화되는 자동 설정들은 부록에서 확인할 수 있다.

다음은 @WebServiceClientTest 어노테이션을 사용하는 예시다:

@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {

    @Autowired
    private MockWebServiceServer server;

    @Autowired
    private SomeWebService someWebService;

    @Test
    void mockServerCall() {
        this.server
            .expect(payload(new StringSource("<request/>")))
            .andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
        assertThat(this.someWebService.test())
            .extracting(Response::getStatus)
            .isEqualTo(200);
    }

}

Additional Auto-configuration and Slicing

각 슬라이스마다 @AutoConfigure… 어노테이션을 하나 이상 제공하는데, 이 어노테이션으로는 말그대로 슬라이스에 포함시켜야 하는 자동 설정을 정의할 수 있다. 커스텀 @AutoConfigure… 어노테이션을 생성하거나 아래 예제처럼 @ImportAutoConfiguration을 테스트에 추가하면, 테스트마다 별도로 필요한 자동 설정을 더 추가할 수 있다:

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {

}

일반 @Import 어노테이션으로 자동 설정을 임포트하지 않도록 주의해라. 자동 설정은 스프링 부트에서 별도 방식으로 다르게 처리한다.

아니면 아래 예제처럼 META-INF/spring.factories에 등록해도 슬라이스 어노테이션을 사용할 때 쓸 별도 자동 설정들을 추가할 수 있다:

org.springframework.boot.test.autoconfigure.jdbc.JdbcTest=com.example.IntegrationAutoConfiguration

슬라이스나 @AutoConfigure… 어노테이션은 @ImportAutoConfiguration을 메타 어노테이션으로 선언만 해주면 이런식으로 커스텀할 수 있다.

User Configuration and Slicing

베스트 프랙티스에 따라 코드 구조를 잡으면 기본적으로 @SpringBootApplication 클래스를 테스트 설정으로 사용한다.

이렇게 되면 중요한 건 애플리케이션의 메인 클래스에, 특정 기능에서만 필요한 설정을 두지 않는 거다.

스프링 배치를 사용 중이고 자동 설정에 의존한다고 가정해보자. 다음과 같이 @SpringBootApplication을 정의할 수 있다:

@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {

    // ...

}

테스트는 이 클래스에서 설정을 가져오기 때문에, 실제로 모든 슬라이스 테스트가 스프링 배치를 시작하려고 하지만, 이는 분명히 원하는 바가 아니다. 다음 예제처럼 특정한 영역에 속하는 설정은 애플리케이션과 동일한 레벨에 있는 별도 @Configuration 클래스로 이동하는 게 좋다:

@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {

    // ...

}

애플리케이션의 복잡성에 따라 커스텀을 위한 @Configuration 클래스가 딱 하나 있을 수도 있고, 도메인 영역마다 클래스가 하나씩 있을 수도 있다. 후자에선 필요하면 @Import 어노테이션으로 테스트에 활성화해줄 수 있다.

테스트 슬라이스는 @Configuration 클래스는 제외하고 스캔한다. @WebMvcTest로 예를 들면, 아래 설정에서 보이는 WebMvcConfigurer 빈은 테스트 슬라이스로 로드하는 애플리케이션 컨텍스트에는 추가되지 않을 거다:

@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {

    @Bean
    public WebMvcConfigurer testConfigurer() {
        return new WebMvcConfigurer() {
            // ...
        };
    }

}

하지만 아래 설정에선 테스트 슬라이스는 커스텀 WebMvcConfigurer를 로드한다.

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    // ...

}

또 하나 헷갈릴 수 있는 건 클래스패스 스캔이다. 코드 구조는 합리적으로 짰지만, 별도의 패키지를 스캔해야 한다고 가정해보자. 애플리케이션은 다음 코드와 유사할 거다:

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

    // ...

}

이렇게 하면 사실상 디폴트 컴포넌트 스캔 지시문을 재정의해서, 슬라이스로 뭘 선택했든지 간에 두 패키지를 스캔하는 부작용을 낳는다. 예를 들어 @DataJpaTest에서는 갑자기 애플리케이션의 컴포넌트와 사용자 설정을 스캔하는 것으로 보일 거다. 다시 말하지만, 커스텀 지시문은 별도의 클래스로 이동시키는 게 이 문제를 해결할 수 있는 길이다.

이게 불가능하다면, 테스트 계층구조 어딘가에 @SpringBootConfiguration을 하나 만들어놓고 대신 사용해도 된다. 아니면 테스트에서 가져올 설정을 지정해서 디폴트 설정을 찾는 동작을 비활성화해도 된다.

Using Spock to Test Spring Boot Applications

스프링 부트 애플리케이션을 테스트할 땐 Spock 2.x를 사용할 수 있다. 먼저 Spock의 spock-spring 모듈 의존성을 애플리케이션 빌드에 추가해라. spock-spring은 스프링의 테스트 프레임워크를 Spock에 통합해준다. 자세한 내용은 Spock 문서에서 스프링 모듈을 확인해봐라.

7.26.4. Test Utilities

애플리케이션 테스트에 유용할만한 몇 가지 테스트 유틸리티 클래스들은 spring-boot에 패키징되어 있다.

ConfigDataApplicationContextInitializer

ConfigDataApplicationContextInitializer는 테스트에서 스프링 부트 application.properties 파일을 로드할 수 있는 ApplicationContextInitializer다. 아래처럼 @SpringBootTest에서 제공하는 기능 셋이 전부 필요하진 않을 때 사용할 수 있다:

@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {

    // ...

}

ConfigDataApplicationContextInitializer만 단독으로 사용하면 @Value("${…}") 주입은 지원하지 않는다. 유일하게 수행하는 작업은 application.properties 파일을 스프링의 Environment에 로드하는 거다. @Value를 사용하려면 별도로 PropertySourcesPlaceholderConfigurer를 설정하거나, 자동으로 설정해주는 @SpringBootTest를 사용해야 한다.

TestPropertyValues

TestPropertyValues를 사용하면 ConfigurableEnvironmentConfigurableApplicationContext에 프로퍼티를 재빠르게 추가할 수 있다. 다음과 같이 key=value 문자열 조합으로 호출하면 된다:

class MyEnvironmentTests {

    @Test
    void testPropertySources() {
        MockEnvironment environment = new MockEnvironment();
        TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
        assertThat(environment.getProperty("name")).isEqualTo("Boot");
    }

}

OutputCapture

OutputCaptureSystem.outSystem.err 출력을 캡처할 수 있는 JUnit Extension이다. 다음과 같이 @ExtendWith(OutputCaptureExtension.class)를 추가하고 CapturedOutput을 테스트 클래스 생성자나 테스트 메소드 인자로 주입해라:

@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {

    @Test
    void testName(CapturedOutput output) {
        System.out.println("Hello World!");
        assertThat(output).contains("World");
    }

}

TestRestTemplate

TestRestTemplate은 통합 테스트에서 스프링의 RestTemplate 대신 좀 더 간편하게 사용할 수 있는 템플릿이다. 순수vanilla 템플릿이나 Basic HTTP 인증(username과 password로)을 전송하는 템플릿을 가져올 수 있다. 이 템플릿은 두 경우 모두 내결함성fault tolerance을 지원한다. 즉, 4xx, 5xx 에러에서 예외를 발생시키지 않고 테스트 친화적인 방식으로 동작한다. 그대신 반환하는 ResponseEntity와, 여기에 들어 있는 상태 코드를 통해 에러를 판단하면 된다.

스프링 프레임워크 5.0은 WebFlux 통합 테스트WebFlux, MVC end-to-end 테스트에 모두 활용할 수 있는 새로운 WebTestClient를 제공한다. WebTestClientTestRestTemplate과는 달리 assertion을 위한 fluent API를 제공한다.

아파치 HTTP 클라이언트(4.3.2 이상)를 사용하는 걸 권장하지만, 필수는 아니다. 아파치 HTTP 클라이언트가 클래스패스에 있으면 TestRestTemplate은 이 클라이언트를 적당히 설정해서 응답해준다. 아파치의 HTTP 클라이언트를 사용하게 되면 테스트 친화적인 기능들이 몇 가지가 더 활성화된다:

TestRestTemplate은 아래 예제처럼 통합 테스트 안에서 직접 인스턴스를 만들어도 된다:

class MyTests {

    private TestRestTemplate template = new TestRestTemplate();

    @Test
    void testRequest() throws Exception {
        ResponseEntity<String> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
        assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
    }

}

아니면 @SpringBootTest 어노테이션으로 WebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORT를 지정하면, 완전한 설정을 갖춘 TestRestTemplate을 주입하고 바로 사용할 수 있다. 필요하면 RestTemplateBuilder 빈으로 별도 커스텀을 적용할 수 있다. 아래 예제처럼 URL에 호스트와 포트를 지정하지 않으면 전부 자동으로 임베디드 서버에 연결된다:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {

    @Autowired
    private TestRestTemplate template;

    @Test
    void testRequest() {
        HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
        assertThat(headers.getLocation()).hasHost("other.example.com");
    }

    @TestConfiguration(proxyBeanMethods = false)
    static class RestTemplateBuilderConfiguration {

        @Bean
        RestTemplateBuilder restTemplateBuilder() {
            return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
                    .setReadTimeout(Duration.ofSeconds(1));
        }

    }

}

전체 목차는 여기에 있습니다.

<< >>

TOP