@RestClientTest?

viiviii·2024년 5월 16일
0

@RestClientTest

외부 API 호출을 위해 RestClient 사용 시 @RestClientTest를 사용하면 편하게 서버를 모킹하여 테스트할 수 있다.

내부적으로 @AutoConfigureMockRestServiceServer를 갖고있어 MockRestServiceServer를 자동 구성해주기 때문이다.

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestTemplateServiceTests {

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

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

}

좀 특이한건 테스트 중인 bean이 RestClient.Builder를 사용한 경우 전체 주소를 테스트 해야 된다.

server.expect(requestTo("https://example.com/greet/details"))
            .andRespond(withSuccess());

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.spring-boot-applications.autoconfigured-rest-client

https://docs.spring.io/spring-framework/reference/testing/spring-mvc-test-client.html#spring-mvc-test-client-resources


✅ in 통합 테스트에서

그럼 서버를 사용하는 테스트에선 어떻게 해야할까?

처음엔 위에서 알게된 @AutoConfigureMockRestServiceServer로 자동 구성해서 쓰면 될 것 같았다.

@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureMockRestServiceServer
class SomeTest {

    @Autowired
    private MockRestServiceServer server;

하지만 에러가 발생한다.

Unable to use auto-configured MockRestServiceServer since mock server customizers have been bound to both a RestTemplate and a RestClient
java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since mock server customizers have been bound to both a RestTemplate and a RestClient

RestTemplate을 쓴 적이 없는데 왜 위 에러가 발생한 걸까?

현재 부트 3.2 기준으로 @SpringBootTestTestRestTemplateWebTestClient bean을 등록해주기 때문이다.

  • Registers a TestRestTemplate and/or WebTestClient bean for use in web tests that are using a fully running web server.

해결 방법은

  • A) @EnableAutoConfiguration(exclude = RestTemplateAutoConfiguration.class) 추가해서 제외하는 방법
  • B) MockRestServiceServer 대신 MockServerRestClientCustomizer를 주입받아 사용하는 방법이 있다

이 외에도 MockRestServiceServer 말고 어떤 라이브러리 추가해서 서버 돌리는 방식을 문서에서 봤었는데 못 찾겠음ㅠ


✏️ 그 외 메모

@AutoConfigureMockRestServiceServer와 @TestConfiguration는 같이 쓸 수 없음

나같은 경우 MockRestServiceServer 관련 코드를 한 곳에 모아두고 싶어서 TestConfiguration로 분리했는데,

여기에서 @AutoConfigureMockRestServiceServer도 포함하고 있으면 좋겠다고 생각했었다.

@TestConfiguration
@AutoConfigureMockRestServiceServer
public SomeConfiguration {...

띠... 안된답니다.

그래서 보니까 @AutoConfigureMockRestServiceServer는 활성화 되어있으면 관련 Configuration이 아래와 같이 bean으로 등록해주길래

@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.test.webclient.mockrestserviceserver", name = "enabled")
public class MockRestServiceServerAutoConfiguration {
	@Bean
	public MockServerRestClientCustomizer mockServerRestClientCustomizer() {
		return new MockServerRestClientCustomizer();
	}
    
    @Bean
	public MockRestServiceServer mockRestServiceServer(MockServerRestTemplateCustomizer restTemplateCustomizer,
			MockServerRestClientCustomizer restClientCustomizer) {
		...
	}

나도 따라서 직접 bean으로 등록해놨다.

@TestConfiguration
public class MyTestConfiguration {

    @Bean
    MockServerRestClientCustomizer myMockServerCustomizer() {
        return new MockServerRestClientCustomizer();
    }

이러면 나중에 등록된 Customizer가 자동구성에 의해 bean으로 등록될 RestClient.Builder를 얻게 되고 그걸 MockRestServiceServer에 바인딩하는 방식이었음

	// RestClientAutoConfiguration.class
	@Bean
	RestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) {
		RestClient.Builder builder = RestClient.builder() ...
		return restClientBuilderConfigurer.configure(builder);
	}
	// MockServerRestClientCustomizer.class
	@Override
	public void customize(RestClient.Builder restClientBuilder) {
		// ...
		MockRestServiceServerBuilder serverBuilder = MockRestServiceServer.bindTo(restClientBuilder);
        // ...
        MockRestServiceServer server = serverBuilder.build(expectationManager);
		this.servers.put(restClientBuilder, server);

솔직히 일단 해놨지만 코드를 볼 때마다 헷갈리고 이게 맞는지도 모르겠음

📌 24.7.13 추가

해당 경계의 인터페이스나 Client 인터페이스를 모킹하도록 변경했음

0개의 댓글