javaunit-testingmockitoresttemplate

JAVA mockito unit test for resttemplate and retryTemplate


I am currently writing unit test for below method

@Autowired
private RequestConfig requestConfig;

@Autowired
private RetryTemplate retryTemplate;

public ResponseEntity<String> makeGetServiceCall(String serviceUrl) throws Exception {
    try {
        return retryTemplate.execute(retryContext -> {

            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = requestConfig.createHttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
            ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.GET, entity, String.class);
            return response;

        });
    } catch (Exception e) {
        throw new Exception("Generic exception while makeGetServiceCall due to" + e + serviceUrl);
    }
}

UPDATED METHOD:

@Autowired
private RequestConfig requestConfig;

@Autowired
private RetryTemplate retryTemplate;

@Autowired
private RestTemplate restTemplate;

public ResponseEntity<String> makeGetServiceCall(String serviceUrl) throws Exception {
    try {
        return retryTemplate.execute(retryContext -> {

            HttpHeaders headers = requestConfig.createHttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
            ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.GET, entity, String.class);
            return response;

        });
    } catch (Exception e) {
        throw new Exception("Generic exception while makeGetServiceCall due to" + e + serviceUrl);
    }
}

I tried all possibilities but I am unable to get it right. Here is my below test.

@Mock
private RestTemplate restTemplate;

@Mock
public RequestConfig requestConfig;

@InjectMocks
private RetryTemplate retryTemplate;

ServiceRequest serviceRequest;


@Test
public void makeGetServiceCall() throws Exception {
    String url = "http://localhost:8080";
    RetryTemplate mockRetryTemplate = Mockito.mock(RetryTemplate.class);
    RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class);
    ResponseEntity<String> myEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
    Mockito.when(mockRetryTemplate.execute(ArgumentMatchers.any(RetryCallback.class), ArgumentMatchers.any(RecoveryCallback.class), ArgumentMatchers.any(RetryState.class))).thenReturn(myEntity);

    Mockito.when(mockRestTemplate.exchange(
            ArgumentMatchers.eq(url),
            ArgumentMatchers.eq(HttpMethod.GET),
            ArgumentMatchers.<HttpEntity<String>>any(),
            ArgumentMatchers.<Class<String>>any())
    ).thenReturn(myEntity);

    ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url);
    Assert.assertEquals(myEntity, response);
}

UPDATED TEST CASE:

 @Mock
public RequestConfig requestConfig;

@Mock
private RestTemplate restTemplate;

@Mock
private RetryTemplate retryTemplate;

@InjectMocks
ServiceRequest serviceRequest;

@Test
public void makeGetServiceCall() throws Exception {
    //given:
    String url = "http://localhost:8080";

    when(requestConfig.createHttpHeaders()).thenReturn(null);
    ResponseEntity<String> myEntity = new ResponseEntity<>( HttpStatus.ACCEPTED);
    when(retryTemplate.execute(any(RetryCallback.class), any(RecoveryCallback.class), any(RetryState.class))).thenAnswer(invocation -> {
        RetryCallback retry = invocation.getArgument(0);
        return retry.doWithRetry(/*here goes RetryContext but it's ignored in ServiceRequest*/null);
    });
    when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(String.class)))
            .thenReturn(myEntity);

    //when:
    ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url);

    //then:
    assertEquals(myEntity, response);
}

The response object which I get from my method call makeGetServiceCall always return null. When I debug the code I see exception org.mockito.exceptions.misusing.WrongTypeOfReturnValue: ResponseEntity cannot be returned by toString() toString() should return String error on the resttemplate mocking where I return myEntity

I am not sure what am I missing.


Solution

  • Well, you have made quite some number of mistakes...

    1. I'm sure you wanted to annotate private RetryTemplate retryTemplate; with @Mock, not @InjectMocks
    2. @InjectMocks should go onto ServiceRequest serviceRequest;
    3. You are defining interactions on some mockRetryTemplate and mockRestTemplate which have nothing to do with serviceRequest. Instead, you should use your @Mock-annotated fields to define interactions on because they are being injected into your object under test (serviceRequest)
    4. Moreover, you can't normally mock RestTemplate and inject it into your ServiceRequest because you don't use dependency injection in the first place for RestTemplate in ServiceRequest. You just instantiate its instance in ServiceRequest.makeGetServiceCall
    5. You are defining an interaction on the wrong method at line Mockito.when(retryTemplate.execute(.... Your interaction specifies RetryTemplate.execute(RetryCallback, RecoveryCallback, RetryState) whereas your ServiceRequest uses another method RetryTemplate.execute(RetryCallback)
    6. You should also notice that RetryTemplate.execute is final and so you can't mock it without extra efforts as explained here. And generally, you should prefer interfaces over classes, e.g. RestOperations and RetryOperations over RestTemplate and RetryTemplate respectively, to be more flexible.

    That said, below is the working test which solves your problem. But take note of removing RestTemplate restTemplate = new RestTemplate(); from ServiceRequest and making restTemplate a field so it's dependency-injected.

    @RunWith(MockitoJUnitRunner.class)
    public class ServiceRequestTest {
        @Mock
        private RestTemplate restTemplate;
    
        @Mock
        public RequestConfig requestConfig;
    
        @Mock
        private RetryTemplate retryTemplate;
    
        @InjectMocks
        ServiceRequest serviceRequest;
    
        @Test
        public void makeGetServiceCall() throws Exception {
            //given:
            String url = "http://localhost:8080";
            ResponseEntity<String> myEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
            when(retryTemplate.execute(any(RetryCallback.class))).thenAnswer(invocation -> {
                RetryCallback retry = invocation.getArgument(0);
                return retry.doWithRetry(/*here goes RetryContext but it's ignored in ServiceRequest*/null);
            });
            when(restTemplate.exchange(eq(url), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class)))
                    .thenReturn(myEntity);
    
            //when:
            ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url);
    
            //then:
            assertEquals(myEntity, response);
        }
    }