Search code examples
springspring-bootgroovyresttemplatespock

How do I unit test a Spring RestTemplate that takes a ResponseExtractor and RequestCallback?


I am developing in Groovy and I am trying to write a Spock unit test for the following use of Spring's RestTemplate...

Included are my request callback and response extractors, and my initialization class of the RestTemplate bean. I am using the ResponseExtractor to stream the response from GET myurl/ and copy it to a file. The RequestCallback is simply setting some headers on the request.

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 framework docs for that particular execute(...) method: 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-

How do I mock out what's happening in these closures? Specifically, I'm interested in mocking out my response extractor because my current test always returns myFile as null.

    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


Solution

  • After you updated your sample code as I requested, I can see more clearly now. You are suffering from a typical (non-)testability problem: Your method getFile does more than just getting a file. It instantiates two dependencies as local variables, making them unmockable and consequently the whole method mostly untestable.

    So you want to refactor for better testability so as to be able to use one testing method I mentioned in my first comment:

    • 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.

    For a more general discussion of testability and how tests drive application design, see my other answer here, sections "General comments" and "Update".

    If any of this is unclear, feel free to ask related(!) follow-up questions.