繁体   English   中英

在单元测试中模拟RestTemplate#postForObject

[英]Mocking RestTemplate#postForObject in a unit test

给定一个EncoderService类,它具有以下createNewStream方法和该方法中使用的一堆常量,如何使用mockitocreateNewStream方法编写单元测试:

public ResponseEntity<Object> createNewStream(Long channelId) {
    String url = IP + VERSION + serverName + VHOSTS + vhostName + APP_NAME + appName + STREAM_FILES;

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
    headers.setAcceptCharset(Arrays.asList(Charset.forName(UTF_8)));

    RestTemplate restTemplate = new RestTemplate();

    String udp = "udp://" + "localhost" + ":" + "1935";
    Map<String, String> map = new HashMap<>();
    map.put("name", STREAMS + appName + channelId);
    map.put("serverName", serverName);
    map.put("uri", udp);
    HttpEntity<Map<String, String>> request = new HttpEntity<>(map, headers);

    HttpStatus statusCode = null;
    try {
        ResponseEntity<Object> response = restTemplate.postForEntity(url, request, Object.class);
        statusCode = response.getStatusCode();
        map.put(MESSAGE, "successful");
        return new ResponseEntity<>(map, statusCode);
    } catch (HttpStatusCodeException e) {
        map.put(MESSAGE, e.getMessage());
        return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
    }
}

RestTemplate是一个类,而不是一个接口,它实现了实际的HTTP传输。 两者都妨碍编写可测试的方法。 最重要的是,您正在构造对OS级别具有副作用而不是注入它的类的实例并不能解决问题。 因此,解决方法是:

  • 根据接口而不是实现来编写方法,在这种情况下为RestOperations
  • 通过构造函数自变量(首选),方法自变量或通过定义为类上字段的Supplier<RestOperations>注入实现RestOperations的实例,例如用于生产的RestTemplate实例
  • 用测试实现或测试中的模拟代替实际实例。 我想去Mockito.mock(RestOperations.class)比较容易,因为像所有其他Spring接口一样, RestOperations定义了太多方法来手动编写测试实现

因此,在EncoderService您可以拥有:

private final RestOperations restClient;

public EncoderService(RestOperations restClient) {
  this.restClient = restClient;
}

public ResponseEntity<Object> createNewStream(Long channelId) {
  ...
  ResponseEntity<Object> response = restClient.postForEntity(...
  ...
}

然后在EncoderServiceTest

ResponseEntity<Object> expectedReturnValue = ...

RestOperations testClient = mock(RestOperations.class);
doReturn(expectedReturnValue).when(testClient).postForEntity(any(), any(), anyClass());

EncoderService service = new EncoderService(testClient);
// use the service

对于其他两种情况,测试设置是完全相同的,只是您将实例传递给方法调用而不是构造函数,或覆盖EncoderService实例上的供应商以返回testClient

我已经回答了一个关于ProcessBuilder的非常相似的问题,该问题在操作系统级别上也有副作用,并且是直接在此处测试的方法中构造的。尝试使用PowerMockito模拟ProcessBuilder的构造函数时出错您可以应用完全相同的策略。

暂无
暂无

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

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