Search code examples
springmockito

After Spring upgrade 3.0.2->3.3.2, Mockito's Mockito.any(Object.class) not mocking Object... (varargs) argument


We have code that executes RestTemplate.exchange() to execute an HTTP request, and a Unit Test that tests it. The expected params (5) are

@Override
public <T> ResponseEntity<T> exchange(String url, 
                                      HttpMethod method,
                                      @Nullable HttpEntity<?> requestEntity, 
                                      Class<T> responseType, 
                                      Object... uriVariables)
throws RestClientException {

The code line to be mocked is below (note the last varargs is omitted, which is OK):

responseEntity = restTemplate.exchange(endpointUrl, 
                                       HttpMethod.POST, 
                                       requestEntity, 
                                       responseType);

Originally in Spring 3.0.2 the following Mockito line mocked the right conditions for us in our unit test:

MockedConstruction<RestTemplate> mockRC = Mockito.mockConstruction(RestTemplate.class,
    (mock, context) -> {
        when(mock.exchange(Mockito.anyString(), 
                           Mockito.any(HttpMethod.class), 
                           Mockito.any(HttpEntity.class), 
                           Mockito.any(Class.class), 
                           Mockito.any(Object.class)
        )).thenThrow(new HttpClientErrorException(HttpStatus.UNPROCESSABLE_ENTITY));

But then after we upgraded to Spring 3.3.2 it no longer mocks correctly. My suspicion is that last varargs param is handled in some new way. Any thoughts?


Solution

  • This is a breaking change in Mockito 5 how varargs arguments are handled by matchers.

    Spring Boot 3.0.2 pulled Mockito 4.8.1 while Spring Boot 3.1.0+ pulls at least Mockito 5.3.1 (Mockito 5.11.0 for current Spring Boot 3.3.2).

    It's also documented in the JavaDoc of the ArgumentMatchers#any(Class) method:

    Notes :

    • For primitive types use anyChar() family.
    • Since Mockito 2.1.0 this method will perform a type check thus null values are not authorized.
    • Since Mockito 2.1.0 any() is no longer an alias of this method.
    • Since Mockito 5.0.0 this method can match varargs if the array type is specified, for example any(String[].class).

    So you will need to change your stub to either of the following:

    1. Match no varargs (empty varargs array) passed – will match your specific call exactly:

       when(mock.exchange(Mockito.anyString(), 
                          Mockito.any(HttpMethod.class), 
                          Mockito.any(HttpEntity.class), 
                          Mockito.any(Class.class)))
      
    2. Match any number of varargs passed – will match any call to the exchange method:

       when(mock.exchange(Mockito.anyString(), 
                          Mockito.any(HttpMethod.class), 
                          Mockito.any(HttpEntity.class), 
                          Mockito.any(Class.class), 
                          Mockito.any(Object[].class)))