首页 > 解决方案 > 如何在 Spring Boot 中模拟 resttemplate 调用?

问题描述

我尝试为调用第 3 方 api 的服务中的其余调用编写测试用例。

@RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
private ForceService forceService;
@Mock
private ForceServiceConfig config;
@Mock
private RestTemplate restTemplate;

@Before
public void setup() {
    forceService = new ForceService(config);
}

@Test
public void apiCall_valid() throws JSONException {
    HttpHeaders headers = new HttpHeaders();
    headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
    headers.set(ACCEPT, APPLICATION_JSON);
    HttpEntity<String> entity = new HttpEntity<>(
            "id=null",
            headers);
    config.authTokenUrl = "https://ex...com/..";
    Mockito.when(restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity, Access.class)).thenReturn(null);
    // Mockito.when(any()).thenReturn(null);
    forceService.apiCall();
}

}

@Component
public class ForceService {
    private ForceServiceConfig config;
    private RestTemplate restTemplate = new RestTemplate();
public ForceService(ForceServiceConfig config) {

    this.config = config;
}
    private String apiCall() {
    HttpHeaders headers = new HttpHeaders();
    headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
    headers.set(ACCEPT, APPLICATION_JSON);

    HttpEntity<String> entity = new HttpEntity<>(
            "&id=" + config.id,
            headers);
    ResponseEntity<Access> response = restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity,
            Access.class);
    return response.getBody().token_type + " " + response.getBody().access_token;
     }

}

我收到以下错误:

org.springframework.web.client.HttpClientErrorException: 404 Not Found at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700 ) 在 org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)

它在测试类中调用 api,我不想发生这种情况。我需要模拟 3rd 方 api 的 resttemplate 调用。我怎么能在不实际调用 api 的情况下做到这一点?

标签: mavenunit-testingspring-bootresttemplatespring-test

解决方案


这就是问题

public class ForceService {
    private ForceServiceConfig config;
    private RestTemplate restTemplate = new RestTemplate(); // HERE

您正在创建新的 REAL rest 模板。你可能想要的是

  1. 使用您在包装测试类中创建的模拟
  2. 使用真正的休息模板和间谍就可以了。

不知道的是您的实际结构(1 个文件 2 个类),但可以安全地假设它不是,并且在任何情况下您都可以简单地将 RestTemplate 作为 ctor 参数传递。所以

@Component
public class ForceService {
    private ForceServiceConfig config;
    private RestTemplate restTemplate;
public ForceService(ForceServiceConfig config,RestTemplate restTemplate) {
    this.restTemplate=restTemplate;
    this.config = config;
}

@Before
public void setup() {
    forceService = new ForceService(config,restTemplate);
}

现在,如果您希望 RestTemplate 只是一个不做任何事情的存根,并且如果没有另行指示,则在任何调用上都返回 null - 将其保留为@Mock.

但是,如果您想让它正常工作并且只拦截一些特定的方法调用和存根响应,请使用 spy.

@Mock
private RestTemplate restTemplate;
private RestTemplate restTemplate=Mockito.mock(RestTemplate.class)

或者

private RestTemplate restTemplate=Mockito.spy(new RestTemplate());

推荐阅读