英文:
Can we use @RestClientTest when the rest template has interceptors using Spring boot 1.5.x?
问题
以下是翻译好的内容:
我正在使用 Spring Boot 1.5.x(Spring 4.2.x),并创建了一个 RestClientSdk 的 Spring 组件类,如下所示:
@Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
@Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//出于简洁起见,省略了其他方法
}
我还定义了一个 DefaultRestTemplateCustomizer 的 Spring 组件,如下所示:
@Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
@Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
@Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
有了这些,我定义了一个测试类,如下所示,它使用了 @RestClientTest 注解:
@RunWith(SpringRunner.class)
@RestClientTest(RestClientSdk.class)
@ActiveProfiles("test")
/*
* RestClientTest 仅包括了最基本的配置,包括一个 rest template builder,
* 所以我们在测试的范围内包含了 rest sdk 的自动配置
*/
@ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
@Autowired
private RestClientSdk restClientSdk;
@Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* 一个简单的 HTTP Get 请求,从一个 REST 服务器检索 JSON 文档,
* 并将其作为响应生成一个普通的 Java 对象。
*/
@Test
public void testPlainHttpGet() throws IOException{
//准备
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"), MediaType.APPLICATION_JSON));
//执行
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//断言
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//为了简洁起见,省略了其他方法
}
----------
**问题**
----------
由于 MockRestServiceServer 构建器用 MockClientHttpRequestFactory 覆盖了我的 rest template 中的 BufferingClientHttpRequestFactory,我从响应中得到了一个空的响应体。这是因为日志拦截器正在读取来自响应的输入流,因此流不再有内容可读取。BufferingClientHttpRequestFactory 将防止这种情况发生。现在,我知道从 Spring 5.0.5 开始,MockRestServiceServer 构建器中有一个名为 *bufferContent* 的额外选项,但我不能选择迁移到 Spring 5.x(Spring Boot 2.x),所以我想知道是否有办法在 Spring Boot 1.5.x / Spring 4.2.x 中进行配置。
提前谢谢您!
Juan
英文:
I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:
@Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
@Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}
I have also defined a DefaultRestTemplateCustomizer spring component as shown here:
@Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
@Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
@Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
With that, I've defined a test class as shown below that uses the @RestClientTest annotation as shown below.
@RunWith(SpringRunner.class)
@RestClientTest(RestClientSdk.class)
@ActiveProfiles("test")
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder,
* so we include the rest sdk auto config within the scope of the test
*/
@ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
@Autowired
private RestClientSdk restClientSdk;
@Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and
* produces a plain old java object as a response.
*/
@Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity
}
PROBLEM
Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.
I thank you in advance!
Juan
答案1
得分: 1
为了解决这个问题,我需要定义一个测试配置,允许我覆盖客户端请求工厂。请参阅下面的代码。这有点巧妙,但我想这里的真正解决方案应该是升级到Spring 5.x / Spring Boot 2.x。
@Configuration
@Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* @param restClientSdk The rest client SDK containing the rest template to use.
* @return The mock rest service server to use.
*/
@Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}
英文:
In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.
@Configuration
@Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* @param restClientSdk The rest client SDK containing the rest template to use.
* @return The mock rest service server to use.
*/
@Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论