如何对采用 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".

部分

如果有任何不清楚的地方,请随时提出相关(!)后续问题。