build method in default implementation of MockRestServiceServerBuilder overwrites request factory which makes impossible to write tests using remote resources access via RestTemplate built through RestTemplateBuilder.defaultHeader() with headers.
How to reproduce:
bean definition:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.defaultHeader("x-api-key", "1234567890").build();
}
using in test:
mockApi = MockRestServiceServer.createServer(restTemplate);
And then requests sent to that mock api will come without custom x-api-key header
@gilinykh Creating RestTemplate as a @Bean often causes problems like this, which is one reason the RestTemplateBuilder was created. I suspect that you're using MockRestServiceServer.bindTo on that singleton bean, which will indeed replace ClientHttpRequestFactory.
Can you please share a complete sample that we can run so that we can confirm if this is a bug or expected behavior.
@philwebb Here's the link to full sample gilinykh/mockrestserver-test
Your summary is correct. But then what's the intended way to write tests with MockRestServiceServer using RestTemplateBuilder and still be able to configure general request properties (e.g. authorization header) only once (at bean creation?). Oh, and I tend to use db access in my tests (e.g. @Sql) and rely on DI working too so @RestClientTest is not of much help here...
IMO, the problem is that since Spring Boot 2.2 there are a number of features that have been moved to wrapping the ClientHttpRequestFactory. Unfortunately, MockRestServiceServer is overriding this and the wrapping that Spring Boot applies is completely ignored.
We changed this because https://github.com/spring-projects/spring-framework/issues/22002 was declined and we wanted to set headers without reading the entire body. I have a feeling we might need that framework change after all.
Maybe we add support for RestTemplateRequestCustomizer, or some such contract, directly in the RestTemplate. That's effectively what an alternative interceptor would have to be in any case due to the way request execution works, which can start before the execute method, e.g. when the body is accessed as OutputStream.
I was working on a PR. I think a ClientHttpRequestInitializer might be a good compromise. We don't actually need to change ClientHttpRequest like the interceptor does, we just need initialize it in a certain way before it's used.
I've reopened https://github.com/spring-projects/spring-framework/issues/22002 with a suggested fix. If that gets accepted to can apply https://github.com/philwebb/spring-boot/tree/gh-17885
It's been accepted. Let me know if you need any help for this.