Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring-Boot RestClientTest not correctly auto-configuring MockRestServiceServer due to unbound RestTemplate

EDIT: This question is specifically pertaining to the @RestClientTest annotation introduced in spring-boot 1.4.0 which is intended to replace the factory method.

Problem:

According to the documentation the @RestClientTest should correctly configure a MockRestServiceServer to use when testing a REST client. However when running a test I am getting an IllegalStateException saying the MockServerRestTemplateCustomizer has not been bound to a RestTemplate.

Its worth noting that I'm using Gson for deserialization and not Jackson, hence the exclude.

Does anyone know how to correctly use this new annotation? I haven't found any examples that require more configuration then when I have already.

Configuration:

@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
public class ClientConfiguration {

...

   @Bean
   public RestTemplateBuilder restTemplateBuilder() {
       return new RestTemplateBuilder()
            .rootUri(rootUri)
            .basicAuthorization(username, password);
   }
}

Client:

@Service
public class ComponentsClientImpl implements ComponentsClient {

    private RestTemplate restTemplate;

    @Autowired
    public ComponentsClientImpl(RestTemplateBuilder builder) {
        this.restTemplate = builder.build();
    }

    public ResponseDTO getComponentDetails(RequestDTO requestDTO) {
        HttpEntity<RequestDTO> entity = new HttpEntity<>(requestDTO);
        ResponseEntity<ResponseDTO> response = 
              restTemplate.postForEntity("/api", entity, ResponseDTO.class);
        return response.getBody();
    }
}

Test

@RunWith(SpringRunner.class)
@RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {

    @Autowired
    private ComponentsClient client;

    @Autowired
    private MockRestServiceServer server;

    @Test
    public void getComponentDetailsWhenResultIsSuccessShouldReturnComponentDetails() throws Exception {
        server.expect(requestTo("/api"))
            .andRespond(withSuccess(getResponseJson(), APPLICATION_JSON));

        ResponseDTO response = client.getComponentDetails(requestDto);
        ResponseDTO expected = responseFromJson(getResponseJson());

        assertThat(response, is(expectedResponse));
    }
}

And the Exception:

java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate

Answer:

As per the answer below there is no need to declare a RestTemplateBuilder bean into the context as it is already provided by the spring-boot auto-configuration.

If the project is a spring-boot application (it has @SpringBootApplication annotation) this will work as intended. In the above case however the project was a client-library and thus had no main application.

In order to ensure the RestTemplateBuilder was injected correctly in the main application context (the bean having been removed) the component scan needs a CUSTOM filter (the one used by @SpringBootApplication)

@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
})
like image 312
Josh C Avatar asked Aug 22 '16 02:08

Josh C


1 Answers

The MockRestServiceServer instance should be constructed from the static factory, using a RestTemplate. See this article for a detailed description of the testing process.

In your example, you can do:

@RunWith(SpringRunner.class)
@RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {

    @Autowired
    private ComponentsClient client;

    @Autowired
    private RestTemplate template;

    private MockRestServiceServer server;

    @Before
    public void setUp() {
        server= MockRestServiceServer.createServer(restTemplate);
    }

    /*Do your test*/
}
like image 143
Deroude Avatar answered Sep 19 '22 23:09

Deroude