Spring-boot: Clarify how to to customize the TestRestTemplate

Created on 26 Jul 2016  Â·  10Comments  Â·  Source: spring-projects/spring-boot

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.

documentation

Most helpful comment

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(...);
}

All 10 comments

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

Was this page helpful?
0 / 5 - 0 ratings