簡體   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