简体   繁体   English

用于 resttemplate 和 retryTemplate 的 JAVA mockito 单元测试

[英]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.我从我的方法调用makeGetServiceCall得到的响应对象总是返回 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当我调试代码时,我看到异常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;我确定您想注释private RetryTemplate retryTemplate; with @Mock , not @InjectMocks使用@Mock ,而不是@InjectMocks
  2. @InjectMocks should go onto ServiceRequest serviceRequest; @InjectMocks应该进入ServiceRequest serviceRequest;
  3. You are defining interactions on some mockRetryTemplate and mockRestTemplate which have nothing to do with serviceRequest .您正在定义一些与serviceRequest无关的mockRetryTemplatemockRestTemplate交互。 Instead, you should use your @Mock -annotated fields to define interactions on because they are being injected into your object under test ( serviceRequest )相反,您应该使用@Mock注释的字段来定义交互,因为它们被注入到您的测试对象中( 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 .此外,您通常不能模拟RestTemplate并将其注入到您的ServiceRequest因为您首先RestTemplateServiceRequest RestTemplate使用依赖注入。 You just instantiate its instance in ServiceRequest.makeGetServiceCall您只需在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)您正在Mockito.when(retryTemplate.execute(... . 您的交互指定RetryTemplate.execute(RetryCallback, RecoveryCallback, RetryState)行上的错误方法上定义交互RetryTemplate.execute(RetryCallback, RecoveryCallback, RetryState)而您的ServiceRequest使用另一种方法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 .你还应该注意到RetryTemplate.execute是最终的,所以你不能像这里解释的那样不做额外的努力来模拟它。 And generally, you should prefer interfaces over classes, eg RestOperations and RetryOperations over RestTemplate and RetryTemplate respectively, to be more flexible.而通常,你应该更喜欢在接口类,例如RestOperationsRetryOperations超过RestTemplateRetryTemplate分别,更灵活。

That said, below is the working test which solves your problem.也就是说,下面是解决您问题的工作测试。 But take note of removing RestTemplate restTemplate = new RestTemplate();但是要注意去掉RestTemplate restTemplate = new RestTemplate(); from ServiceRequest and making restTemplate a field so it's dependency-injected.来自ServiceRequest并使restTemplate成为一个字段,因此它是依赖注入的。

@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对我来说,下面的工作,否则,它总是返回 null

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;和 import 是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));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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