简体   繁体   English

在单元测试中模拟RestTemplate#postForObject

[英]Mocking RestTemplate#postForObject in a unit test

Given a class EncoderService which has the following createNewStream method and a bunch of constants used in the method, how can I use mockito to write a unit-test for the createNewStream method: 给定一个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 is a class, not an interface, and it implements the actual HTTP transport. RestTemplate是一个类,而不是一个接口,它实现了实际的HTTP传输。 Both are standing in the way of writing a testable method. 两者都妨碍编写可测试的方法。 On top of that the fact that you are constructing an instance of a class that has side effects on the OS level rather than getting it injected does not help the case. 最重要的是,您正在构造对OS级别具有副作用而不是注入它的类的实例并不能解决问题。 So the way to solve it is: 因此,解决方法是:

  • write your method based around an interface rather than an implementation, RestOperations in this case 根据接口而不是实现来编写方法,在这种情况下为RestOperations
  • inject an instance implementing RestOperations , eg an instance of RestTemplate for production, via a constructor argument (preferred), method argument or via a Supplier<RestOperations> defined as a field on the class 通过构造函数自变量(首选),方法自变量或通过定义为类上字段的Supplier<RestOperations>注入实现RestOperations的实例,例如用于生产的RestTemplate实例
  • substitute an actual instance with a test implementation or a mock in test. 用测试实现或测试中的模拟代替实际实例。 I guess it is easier to go for a Mockito.mock(RestOperations.class) because RestOperations just like all other Spring interfaces defines way too many method for writing a test implementation manually 我想去Mockito.mock(RestOperations.class)比较容易,因为像所有其他Spring接口一样, RestOperations定义了太多方法来手动编写测试实现

So in EncoderService you can have: 因此,在EncoderService您可以拥有:

private final RestOperations restClient;

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

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

And then in EncoderServiceTest : 然后在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

For the other two cases the test setup is exactly the same, just you would pass the instance into the method call instead of constructor or overwrite the supplier on the EncoderService instance to return the testClient . 对于其他两种情况,测试设置是完全相同的,只是您将实例传递给方法调用而不是构造函数,或覆盖EncoderService实例上的供应商以返回testClient

I have answered a very similar question about ProcessBuilder which also has side effects on the OS level and was constructed directly in the method under test here Error trying to mock constructor for ProcessBuilder using PowerMockito You can apply exactly the same tactics. 我已经回答了一个关于ProcessBuilder的非常相似的问题,该问题在操作系统级别上也有副作用,并且是直接在此处测试的方法中构造的。尝试使用PowerMockito模拟ProcessBuilder的构造函数时出错您可以应用完全相同的策略。

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

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