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

Spring Mockito测试RestTemplate.postForEntity抛出URI非绝对路径异常

问题根本原因

你的MyServiceImpl类中postCar方法每次执行时都会手动new RestTemplate()生成全新的实例,完全没有使用单元测试中Mock好并注入到Service的restTemplateMock,因此你定义的Mock规则全部不生效,代码仍然会用新生成的RestTemplate发起真实网络请求,自然会依赖外部资源、不符合单元测试的隔离要求。

修复步骤
  • 第一步:修改Service代码,将RestTemplate改为注入的Bean,禁止在方法内手动创建
@Service
public class MyServiceImpl implements MyService {

    @Value("${myUri}")
    private String uri;

    // 改为注入RestTemplate,不要在方法中new
    @Autowired
    private RestTemplate restTemplate;

    public void setUri(String uri) { this.uri = uri; }
    // 新增set方法方便单元测试注入Mock实例
    public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; }

    @Override
    public ResponseEntity<CarResponse> postCar(CarRequest carRequest, HttpHeaders httpHeaders) {
        List<String> authHeader = httpHeaders.get("authorization");
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", authHeader.get(0));
        HttpEntity<CarRequest> request = new HttpEntity<>(carRequest, headers);
        // 使用注入的restTemplate发起请求
        ResponseEntity<CarResponse> carResponse = restTemplate.postForEntity(uri, request, CarResponse.class);
        return carResponse;
    }
}
  • 第二步:修正单元测试代码的笔误与逻辑问题
public class CarServiceTest {

    @InjectMocks
    MyServiceImpl carServiceSut;

    @Mock
    RestTemplate restTemplateMock;

    CarResponse carResponseFake = new CarResponse();
    // 提前初始化测试用的请求对象
    CarRequest carRequestFake = new CarRequest();

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        carResponseFake.setCarVin(12345);
        // 统一注入测试用的假URI
        carServiceSut.setUri("http://FAKE/URI/myapi/cars");
    }

    @Test
    void testPostCar() {
        // Arrange
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "test-token");
        ResponseEntity<CarResponse> expectedResponse = new ResponseEntity<>(carResponseFake, HttpStatus.OK);

        when(restTemplateMock.postForEntity(
            eq("http://FAKE/URI/myapi/cars"),
            Mockito.any(HttpEntity.class),
            eq(CarResponse.class)
        )).thenReturn(expectedResponse);

        // Act
        ResponseEntity<CarResponse> actualResponse = carServiceSut.postCar(carRequestFake, headers);

        // Assert
        assertEquals(12345, actualResponse.getBody().getCarVin());
        // 额外验证RestTemplate的方法确实被调用了1次,确认Mock生效
        Mockito.verify(restTemplateMock, Mockito.times(1)).postForEntity(
            eq("http://FAKE/URI/myapi/cars"),
            Mockito.any(HttpEntity.class),
            eq(CarResponse.class)
        );
    }
}
额外优化建议
  • 可以在Spring配置类中注册全局的RestTemplate Bean,避免重复创建实例,也方便统一配置超时时间、拦截器等公共参数
  • 修复代码中现存的笔误:比如Service方法返回值拼写错误cardResponse、Controller调用的getCard方法和Service实际的postCar方法不匹配等问题,避免运行时报错
  • 调整后的单元测试完全不需要加载Spring上下文,纯Mockito运行速度极快,也不会依赖任何外部资源和配置文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 13:45:04