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

uda*_*486 5 java unit-testing mockito resttemplate

我目前正在为以下方法编写单元测试

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新方法:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试了所有可能性,但我无法做对。这是我的以下测试。

@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);
}
Run Code Online (Sandbox Code Playgroud)

更新的测试用例:

 @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);
}
Run Code Online (Sandbox Code Playgroud)

我从我的方法调用中得到的响应对象makeGetServiceCall总是返回 null。当我调试代码时,我在返回org.mockito.exceptions.misusing.WrongTypeOfReturnValue: ResponseEntity cannot be returned by toString() toString() should return Stringresttemplate模拟中看到异常错误myEntity

我不确定我错过了什么。

Dmi*_*tov 10

好吧,你已经犯了很多错误了......

  1. 我确定您想用private RetryTemplate retryTemplate;来注释@Mock,而不是@InjectMocks
  2. @InjectMocks应该继续ServiceRequest serviceRequest;
  3. 您正在定义一些mockRetryTemplatemockRestTemplate无关的交互serviceRequest。相反,您应该使用@Mock-annotated 字段来定义交互,因为它们被注入到您的测试对象中 ( serviceRequest)
  4. 此外,您通常不能模拟RestTemplate并将其注入到您的代码中ServiceRequest,因为您一开始就没有在RestTemplatein中使用依赖项注入ServiceRequest。你只需实例化它的实例ServiceRequest.makeGetServiceCall
  5. 您在 line 的错误方法上定义了交互Mockito.when(retryTemplate.execute(...。您的交互指定RetryTemplate.execute(RetryCallback, RecoveryCallback, RetryState),而您ServiceRequest使用另一种方法RetryTemplate.execute(RetryCallback)
  6. 您还应该注意到这RetryTemplate.execute是最终的,因此如果不付出额外的努力就无法嘲笑它,如此处所述。一般来说,您应该更喜欢接口而不是类,例如RestOperationsRetryOperations分别超过RestTemplateRetryTemplate,这样更灵活。

也就是说,下面是解决您问题的工作测试。但请注意删除RestTemplate restTemplate = new RestTemplate();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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢分享。但我收到错误:`org.mockito.exceptions.misusing.InvalidUseOfMatchersException:参数匹配器的使用无效!预期有 3 个匹配器,记录了 1 个:` 传递给restTemplate 交换的参数 (3认同)

Ali*_*786 6

对我来说,下面有效,否则,它总是返回 null

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

进口是 import static org.mockito.ArgumentMatchers.any;