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
相关产品推荐
相关产品推荐

