如何对采用 ResponseExtractor 和 RequestCallback 的 Spring RestTemplate 进行单元测试?
How do I unit test a Spring RestTemplate that takes a ResponseExtractor and RequestCallback?
我正在 Groovy 进行开发,我正在尝试编写一个 Spock 单元测试以用于 Spring 的 RestTemplate...
包括我的请求回调和响应提取器,以及我对 RestTemplate bean 的初始化 class。我正在使用 ResponseExtractor 流式传输来自 GET myurl/
的响应并将其复制到文件中。 RequestCallback 只是在请求上设置一些 headers。
class RestTemplateConfig() {
@Bean(name = 'myRestTemplate')
RestTemplate getMyRestTemplate() {
RestTemplate restTemplate = new RestTemplateBuilder().build()
return restTemplate
}
}
class MyClass() {
@Autowired
@Qualifier('myRestTemplate')
RestTemplate restTemplate
File getFile() {
ResponseExtractor<Void> responseExtractor = { ClientHttpResponse response ->
// do something with the response
// in this case, the response is an input stream so we copy the input stream to a file
myFile = response.getBody() // roughly, in a psuedocode-ish way
return null
}
RequestCallback requestCallback = { ClientHttpRequest request ->
request.getHeaders().setAccept([MediaType.APPLICATION_JSON])
}
File myFile
// get my file data
restTemplate.execute('myurl/', HttpMethod.GET, requestCallback, responseExtractor)
return myFile
}
}
Spring 该特定 execute(...)
方法的框架文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#execute-java.net.URI-org.springframework.http.HttpMethod-org.springframework.web.client.RequestCallback-org.springframework.web.client.ResponseExtractor-
如何模拟这些闭包中发生的事情?具体来说,我有兴趣模拟我的响应提取器,因为我当前的测试总是 returns myFile
为空。
when:
// do stuff
then:
1 * restTemplate.execute('myurl/, HttpMethod.GET, _, _) // how can I mock out the expected response here?
0 * _
myFile != null // this fails because myFile is null
按照我的要求更新了示例代码后,我现在可以看得更清楚了。您遇到了典型的(非)可测试性问题:您的方法 getFile
不仅仅是获取文件。它将两个依赖项实例化为局部变量,使它们不可模拟,因此整个方法几乎不可测试。
所以您想重构以获得更好的可测试性,以便能够使用我在第一条评论中提到的一种测试方法:
- If the
requestCallback
and responseExtractor
can be injected via constructor or setter, you can inject mocks.
- If they are created by some kind of factory class, you can stub that class.
- In case of a factory method inside the class under test itself you can use a spy on the class and stub the factory method.
有关可测试性和测试如何推动应用程序设计的更一般性讨论,请参阅我的其他回答 ,第 "General comments" 和 "Update".
部分
如果有任何不清楚的地方,请随时提出相关(!)后续问题。
我正在 Groovy 进行开发,我正在尝试编写一个 Spock 单元测试以用于 Spring 的 RestTemplate...
包括我的请求回调和响应提取器,以及我对 RestTemplate bean 的初始化 class。我正在使用 ResponseExtractor 流式传输来自 GET myurl/
的响应并将其复制到文件中。 RequestCallback 只是在请求上设置一些 headers。
class RestTemplateConfig() {
@Bean(name = 'myRestTemplate')
RestTemplate getMyRestTemplate() {
RestTemplate restTemplate = new RestTemplateBuilder().build()
return restTemplate
}
}
class MyClass() {
@Autowired
@Qualifier('myRestTemplate')
RestTemplate restTemplate
File getFile() {
ResponseExtractor<Void> responseExtractor = { ClientHttpResponse response ->
// do something with the response
// in this case, the response is an input stream so we copy the input stream to a file
myFile = response.getBody() // roughly, in a psuedocode-ish way
return null
}
RequestCallback requestCallback = { ClientHttpRequest request ->
request.getHeaders().setAccept([MediaType.APPLICATION_JSON])
}
File myFile
// get my file data
restTemplate.execute('myurl/', HttpMethod.GET, requestCallback, responseExtractor)
return myFile
}
}
Spring 该特定 execute(...)
方法的框架文档:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#execute-java.net.URI-org.springframework.http.HttpMethod-org.springframework.web.client.RequestCallback-org.springframework.web.client.ResponseExtractor-
如何模拟这些闭包中发生的事情?具体来说,我有兴趣模拟我的响应提取器,因为我当前的测试总是 returns myFile
为空。
when:
// do stuff
then:
1 * restTemplate.execute('myurl/, HttpMethod.GET, _, _) // how can I mock out the expected response here?
0 * _
myFile != null // this fails because myFile is null
按照我的要求更新了示例代码后,我现在可以看得更清楚了。您遇到了典型的(非)可测试性问题:您的方法 getFile
不仅仅是获取文件。它将两个依赖项实例化为局部变量,使它们不可模拟,因此整个方法几乎不可测试。
所以您想重构以获得更好的可测试性,以便能够使用我在第一条评论中提到的一种测试方法:
- If the
requestCallback
andresponseExtractor
can be injected via constructor or setter, you can inject mocks.- If they are created by some kind of factory class, you can stub that class.
- In case of a factory method inside the class under test itself you can use a spy on the class and stub the factory method.
有关可测试性和测试如何推动应用程序设计的更一般性讨论,请参阅我的其他回答
如果有任何不清楚的地方,请随时提出相关(!)后续问题。