java - 模拟 RestTemplate API 调用

标签 java spring spring-boot mockito resttemplate

我需要对使用 RestTemplate 进行外部 API 调用的方法进行单元测试,我试图模拟外部 API 的响应并断言该方法的返回值。我还尝试使用 MockRestServiceServer@InjectMocks 模拟响应,但无法获得预期的输出,并且它进行了实际的 API 调用。任何指导将不胜感激。


测试用例

public class SnowFlakeServiceTest {

  private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
  private final SnowFlakeServiceImpl snowFlakeService = new SnowFlakeServiceImpl(restTemplate);

  @Test
  public void testGetAccessToken() {

    SnowFlakeTokenDTO snowFlakeTokenDTO = new SnowFlakeTokenDTO();
    snowFlakeTokenDTO.setAccessToken("fakeAccessToken");
    snowFlakeTokenDTO.setExpiresIn(600);
    snowFlakeTokenDTO.setTokenType("Bearer");

    ResponseEntity<SnowFlakeTokenDTO> responseEntity = new ResponseEntity<>(snowFlakeTokenDTO, HttpStatus.OK);
    when(restTemplate.exchange(
        ArgumentMatchers.anyString(),
        ArgumentMatchers.any(HttpMethod.class),
        ArgumentMatchers.any(),
        ArgumentMatchers.<Class<SnowFlakeTokenDTO>>any()))
        .thenReturn(responseEntity);

    assertThat(snowFlakeService.getAccessToken()).isEqualTo(snowFlakeTokenDTO.getAccessToken());
  }

}

SnowFlakeServiceImpl.java

 private final RestTemplate restTemplate;

  public SnowFlakeServiceImpl(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }

@Override
  public String getAccessToken() {
    log.debug("token requested id : {}, secret :{}", snowFlakeClientId, snowFlakeClientSecret);
    try {
      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
      httpHeaders.setBasicAuth(snowFlakeClientId, snowFlakeClientSecret);

      MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
      requestBody.add("grant_type", snowFlakeRefreshTokenGrantType);
      requestBody.add("refresh_token", refreshToken);
      requestBody.add("redirect_uri", snowFlakeRedirectUrl);

      HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestBody, httpHeaders);
      ResponseEntity<SnowFlakeTokenDTO> response = restTemplate.exchange(snowFlakeTokenRequestUrl, HttpMethod.POST, request, SnowFlakeTokenDTO.class);
      log.debug("response: {}", response);
      return Objects.requireNonNull(response.getBody()).getAccessToken();
    } catch (Exception e) {
      log.debug("error: {}", ExceptionUtils.getRootCauseMessage(e));
      throw new ErrorDTO(Status.BAD_REQUEST, accessTokenErrorMessage, e.getMessage());
    }
  }

RestTemplateConigurations.java

@Configuration
public class RestTemplateConfiguration {

  @Value("${rest-template.connection.timeout}")
  private int connectionTimeout;

  @Value("${rest-template.read.timeout}")
  private int readTimeout;

  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
        .setConnectTimeout(Duration.ofMillis(connectionTimeout))
        .setReadTimeout(Duration.ofMillis(readTimeout))
        .build();
  }
}

Spring Boot:2.5.4
java :11
朱尼特:5

最佳答案

您不需要每次调用 getAccessToken() 方法时都需要一个新的 RestTemplate。它应该由 Spring 注入(inject):

@Service
public class SnowFlakeService {

  private RestTemplate restTemplate;

  public ServiceImpl(RestTemplate restTemplate) {
      this.restTemplate = restTemplate;
  }

  @Override
  public String getAccessToken() {
    try {
      HttpHeaders httpHeaders = new HttpHeaders();
      httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
      httpHeaders.setBasicAuth(snowFlakeClientId, snowFlakeClientSecret);

      MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
      requestBody.add("grant_type", snowFlakeRefreshTokenGrantType);
      requestBody.add("refresh_token", refreshToken);
      requestBody.add("redirect_uri", snowFlakeRedirectUrl);

      HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(requestBody, httpHeaders);
      ResponseEntity<SnowFlakeTokenDTO> response = restTemplate.exchange(snowFlakeTokenRequestUrl, HttpMethod.POST, request, SnowFlakeTokenDTO.class);
      
      return Objects.requireNonNull(response.getBody()).getAccessToken();
    } catch (Exception e) {
      throw new ErrorDTO(Status.BAD_REQUEST, accessTokenErrorMessage, e.getMessage());
    }
  }
}

除此之外,您实际上并不需要 @SpringBootTest 来测试 SnowFlakeService。常规的单元测试就可以了。

public class SnowFlakeServiceTest {

  private RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
  private SnowFlakeService snowFlakeService = new SnowFlakeService(restTemplate);

  @Test
  public void testGetAccessToken() {
    SnowFlakeTokenDTO snowFlakeTokenDTO = new SnowFlakeTokenDTO();
    snowFlakeTokenDTO.setAccessToken("fakeAccessToken");
    snowFlakeTokenDTO.setExpiresIn(600);
    snowFlakeTokenDTO.setTokenType("Bearer");

    ResponseEntity<SnowFlakeTokenDTO> responseEntity = new ResponseEntity<>(snowFlakeTokenDTO, HttpStatus.OK);
    when(restTemplate.exchange(
        ArgumentMatchers.anyString(),
        ArgumentMatchers.any(HttpMethod.class),
        ArgumentMatchers.any(),
        ArgumentMatchers.<Class<SnowFlakeTokenDTO>>any()))
        .thenReturn(responseEntity);

    assertThat(snowFlakeService.getAccessToken()).isEqualTo(snowFlakeTokenDTO.getAccessToken());
  }

}

关于java - 模拟 RestTemplate API 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70035439/

相关文章:

Spring-Boot Elasticseach EntityMapper 无法 Autowiring

spring-boot - Junit5-木星所有测试套件@BeforeAll @AfterAll 不起作用

java - 循环切换以打印输出值

java - 为什么零长度字符总是保留在 java 正则表达式模式 a 的源字符串的末尾?

java - Liferay:根据组织更改用户登录页面

java - Servlet 被初始化不止一次?

java - 换行后字节不相等

html - Thymeleaf 下拉菜单中的默认值

java - 如何在 Webflux 功能端点测试中禁用 Spring Security

spring-boot - 休息 Controller 通过Spring kafka返回kafka中的记录