简体   繁体   中英

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.

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, eg 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);
    }
}

For me below worked, otherwise, it was returning null always

when(retryTemplate.execute(any(),any(),any())).thenAnswer(invocation -> {
        RetryCallback retry = invocation.getArgument(0);
        return retry.doWithRetry(null);
});

and import was import static org.mockito.ArgumentMatchers.any;

Generic solution:

Mockito.when(retryTemplate.execute(Matchers.any(),Matchers.any(),Matchers.any())).thenAnswer(invocation -> {
            RetryCallback retry = invocation.getArgumentAt(0,Matchers.any());
            return retry.doWithRetry(null);
        });

It works for me!

@ExtendWith(MockitoExtension.class)
class RetryableRestClientTest {

    @Mock
    private RestTemplate restTemplate;

    @Mock
    private RetryTemplate retryTemplate;

    @InjectMocks
    private RetryableRestClient client;

    @SuppressWarnings("rawtypes")
    @Test
    void test_get() {

        String url = "https://faked-url";

        ResponseEntity<String> expectedResponseEntity = new ResponseEntity<>(HttpStatus.OK);

        Mockito.when(retryTemplate.execute(Mockito.any(), Mockito.any(), Mockito.any()))
                .thenAnswer(invocation -> {
                    RetryCallback retry = invocation.getArgument(0);
                    return retry.doWithRetry(null);
                });

        Mockito.when(restTemplate.exchange(Mockito.eq(url), Mockito.eq(HttpMethod.GET), Mockito.any(HttpEntity.class), Mockito.eq(String.class)))
                .thenReturn(expectedResponseEntity);

        ResponseEntity<String> actualResponseEntity = client.get(url);

        Assertions.assertEquals(expectedResponseEntity, actualResponseEntity);

    }
}

@Component
public class RetryableRestClient {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    private RestTemplate restTemplate;

    private HttpHeaders fakeHttpHeaders() {
        HttpHeaders headers = new HttpHeaders();
        // fake browser's behavior
        headers.add("authority", "m.nowscore.com");
        headers.add("cache-control", "max-age=0");
        headers.add("sec-ch-ua", "\" Not;A Brand\";v=\"99\", \"Google Chrome\";v=\"91\", \"Chromium\";v=\"91\"");
        headers.add("sec-ch-ua-mobile", "?0");
        headers.add("upgrade-insecure-requests", "1");
        headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36");
        headers.add("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
        headers.add("sec-fetch-site", "none");
        headers.add("sec-fetch-mode", "navigate");
        headers.add("sec-fetch-user", "?1");
        headers.add("sec-fetch-dest", "document");
        headers.add("accept-language", "en-US,en;q=0.9");
        return headers;
    }

    public final ResponseEntity<String> get(String url) {
        return retryTemplate.execute(context -> restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, fakeHttpHeaders()), String.class));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM