The documentation is pretty silent on how to customize TestRestTemplate (e.g. register additional HttpMessageConverters). I've discovered RestTemplateCustomizer but declaring a Spring bean for that doesn't seem to have any effect. Declaring a bean of type RestTemplate and customizing that through a RestTemplateBuilder doesn't either.
I finally tried this:
@Bean
TestRestTemplate template(RestTemplateBuilder builder) {
return new TestRestTemplate(
builder.additionalMessageConverters(new ProjectingJackson2HttpMessageConverter()).build());
}
But that lets the bootstrap fail as I now end up with two TestRestTemplate instances. Making the manually declared one @Primary makes the context bootstrap, but the RestTemplate created then doesn't seem to support non-absolute URIs.
I am aware that in my particular example I can declare HttpMessageConverter beans but they then get registered with both the server side MVC setup _and_ the client.
TestRestTemplateFactory.setApplicationContext(…) gives a hint at how to solve that: declare a bean of type RestTemplateBuilder:
@Bean
RestTemplateBuilder builder() {
return new RestTemplateBuilder().additionalMessageConverters(new ProjectingJackson2HttpMessageConverter());
}
So I guess it's just a matter of documenting how RestTemplateBuilder is supposed to be used and maybe a tiny hint in TestRestTemplate to that.
Thanks, Phil!
Where do I need to declare the RestTemplateBuilder bean? I added a configuration class for my tests that has the bean definition, but it is initialized too late and not used in SpringBootTestContextCustomizer.
@FrontierPsychiatrist That doesn't sound right. Are you sure your configuration class is being picked up? The example in the documentation works for me.
Yes, I copied that example. Two breakpoints, one in SpringBootTestContextCustomizer and one in my @Bean method show both are called, but my bean is created too late.
I will try to create a more minimal example (the application also has the Spring Cloud Stream support test starter) tomorrow.
Ok, it was (luckily :)) my error.
I didn't see that the RestTemplateBuilder is immutable and the methods will always return a new instance. So I set an additional message converter and wondered why it was never used - but in fact it wasn't even in the builder.
I misinterpreted the lazy bean initiliazition via applicationContext.getBean(...) as being too late while it was actually calling my bean producer method.
So if anyone ever has a similar problem, don't do this
@Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder builder = new RestTemplateBuilder();
builder.additionalMessageConverters(...);
return builder;
}
it won't work.
Instead do this
@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().additionalMessageConverters(...);
}
@FrontierPsychiatrist Thanks for adding the note
My bean is never called? Why?
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // This is so cool. This automatically sets up everything and starts the server. *like*
public class RestEndpointTests {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
TestRestTemplate client;
@LocalServerPort
int localServerPort;
@Bean
RestTemplateBuilder restTemplateBuilder() {
log.trace("========== resteTemplateBuilder" + localServerPort); // this gets never called. WHY?
return new RestTemplateBuilder();
}
}
@doogiemuc this issue is closed. Please ask questions on stackoverflow.
I think you would need to add this to a @Configuration class. And also, ask on stack overflow next time :p
Most helpful comment
Ok, it was (luckily :)) my error.
I didn't see that the
RestTemplateBuilderis immutable and the methods will always return a new instance. So I set an additional message converter and wondered why it was never used - but in fact it wasn't even in the builder.I misinterpreted the lazy bean initiliazition via
applicationContext.getBean(...)as being too late while it was actually calling my bean producer method.So if anyone ever has a similar problem, don't do this
it won't work.
Instead do this