You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mockito测试RestTemplate.exchange抛出URI非绝对异常的解决求助

解决Mockito测试RestTemplate.exchange时的URI非绝对路径异常

核心问题

你的RestTemplateWrapper类中,每次调用方法都会新建一个RestTemplate实例(RestTemplate restTemplate = new RestTemplate();),这导致你在测试类中Mock的RestTemplate根本无法注入到Wrapper类里。测试时实际执行的是真实RestTemplate的exchange方法,而你传入的URL是空字符串"",自然触发了URI is not absolute异常。

修复步骤

1. 修改RestTemplateWrapper类,改为依赖注入RestTemplate

把RestTemplate从方法内新建改为类级别的依赖注入,这样测试时才能替换为Mock对象(推荐构造注入,符合Spring官方最佳实践):

@Component
public class RestTemplateWrapper {
  private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateWrapper.class);
  
  private final RestTemplate restTemplate;

  // 构造注入RestTemplate
  public RestTemplateWrapper(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }
  
  public <T> ResponseEntity<T> callWebServiceGET(String url,HttpEntity<?> httpEntity,
      ParameterizedTypeReference<T> parameterizedTypeReference) {

    ResponseEntity<T> response=null;
    LOGGER.trace("Entered callWebServiceGET");
    LOGGER.info("Calling WebService {}", url);
    try {
      response=restTemplate.exchange(url, HttpMethod.GET, httpEntity, parameterizedTypeReference);
    } catch (HttpClientErrorException e) {
      if (HttpStatus.NOT_FOUND.equals(e.getStatusCode())) {
        LOGGER.error("Service Unavailable - Code 404 returned. " + url + e.getMessage());
      } else if (HttpStatus.UNAUTHORIZED.equals(e.getStatusCode())) {
        LOGGER.error("Token Expired- Code 401 returned. " + e.getMessage());
      } else if (HttpStatus.BAD_REQUEST.equals(e.getStatusCode())) {
        LOGGER.error("Bad Input, 400 returned.{} {} ", url , e.getMessage(), e);
      } else {
        LOGGER.error("WEB Service Failure. " + e.getMessage());
      }
    }
    return response;
  }
}

2. 调整测试用例,匹配正确参数

测试时传入的URL要和Mock时指定的URL一致,同时用宽松的匹配器避免参数不匹配导致Mock失效:

public class RestTemplateWrapperTest  {
  
  @Mock
  private RestTemplate mockRestTemplate;
  
  @InjectMocks
  private RestTemplateWrapper webUtils;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void callWebServiceGET_OK()  {
    String testUrl = "/objects/get-objectA";
    HttpEntity<String> httpEntity= new ResponseEntity<>(HttpStatus.OK);
    ResponseEntity<String> entityResponse=new ResponseEntity<>("MOCK_RESPONSE", HttpStatus.OK);
    
    // 配置Mock行为,用宽松匹配器兼容参数差异
    when(mockRestTemplate.exchange(
        eq(testUrl), 
        eq(HttpMethod.GET), 
        any(HttpEntity.class), 
        any(ParameterizedTypeReference.class)
    )).thenReturn(entityResponse);
    
    // 调用方法时传入合法参数
    ResponseEntity<String> mockResponse= webUtils.callWebServiceGET(
        testUrl, 
        httpEntity,  
        new ParameterizedTypeReference<String>(){}
    );
    
    Assert.assertEquals("MOCK_RESPONSE", mockResponse.getBody());
    // 验证Mock对象确实被调用
    verify(mockRestTemplate, times(1)).exchange(
        eq(testUrl), 
        eq(HttpMethod.GET), 
        any(HttpEntity.class), 
        any(ParameterizedTypeReference.class)
    );
  }
}

额外说明

  • 如果你一定要保留方法内新建RestTemplate的写法(不推荐),需要用PowerMockMock来拦截new RestTemplate()的实例化行为,但这会增加测试复杂度,Spring项目中更推荐依赖注入的方式。
  • 测试时尽量避免传入空字符串作为URL,即使是Mock场景,保持参数合理性也能减少不必要的异常。

内容的提问来源于stack exchange,提问作者Oswaldo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:56:13