【问题标题】:Mocking RestTemplate#postForObject in a unit test在单元测试中模拟 RestTemplate#postForObject
【发布时间】:2018-05-09 04:18:08
【问题描述】:

给定一个类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);
    }
}

【问题讨论】:

  • 请编辑您的问题以删除重复项,并说明您要测试的功能的哪些方面,以及您遇到的问题。
  • 请看下面我的回答。否则,为了提出一个易于理解的问题,您应该摆脱上面的所有静态声明,使您的示例尽可能小。

标签: java unit-testing mockito resttemplate


【解决方案1】:

RestTemplate 是一个类,而不是一个接口,它实现了实际的 HTTP 传输。两者都阻碍了编写可测试的方法。最重要的是,您正在构建一个对操作系统级别有副作用的类的实例,而不是注入它,这一事实无济于事。所以解决的方法是:

  • 围绕接口而不是实现编写方法,在本例中为RestOperations
  • 注入一个实现RestOperations的实例,例如用于生产的RestTemplate 实例,通过构造函数参数(首选)、方法参数或通过定义为类上的字段的Supplier&lt;RestOperations&gt;
  • 用测试实现或模拟测试替换实际实例。我想使用Mockito.mock(RestOperations.class) 更容易,因为RestOperations 就像所有其他 Spring 接口一样定义了太多手动编写测试实现的方法

所以在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 的非常相似的问题,它对操作系统级别也有副作用,并且是直接在此处测试的方法中构造的Error trying to mock constructor for ProcessBuilder using PowerMockito 您可以应用完全相同的策略。

【讨论】:

    猜你喜欢
    • 2021-11-19
    • 2016-01-29
    • 2019-12-13
    • 2020-05-03
    • 2014-07-08
    • 2017-02-09
    • 2018-11-27
    • 1970-01-01
    • 2015-05-04
    相关资源
    最近更新 更多