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

RestTemplate getBody()返回空指针异常,Mock方式是否有误?

Mocking RestTemplate导致getBody()空指针异常的问题分析与修复

嘿,我来帮你排查这个问题——你的RestTemplate Mock写法存在几个关键错误,直接导致了getBody()抛出空指针。咱们一步步拆解问题,然后给出修复方案:

问题回顾

你有一个调用REST服务的MyService类:

class MyService{
  RestTemplate restTemplate = new RestTemplate();

  public void handler(){
    String token;
    token = restTemplate.exchange(authurl, HttpMethod.POST, getHttpEntity(tenantLogin, basicAuth), String.class)
          .getBody();
  }

  private static <T> HttpEntity<T> getHttpEntity(T jsonRequest, String authorization) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.set("Authorization", authorization);
    return new HttpEntity<T>(jsonRequest, headers);
  }
}

测试类尝试Mock RestTemplate,但运行时getBody()抛出空指针:

class RestTemplateTest{

  RestTemplate restTemplate = mock(RestTemplate.class);

  Field fieldReflectionUtil = 
  ReflectionUtils.findField(MyService.class, "restTemplate");
    ReflectionUtils.makeAccessible(fieldReflectionUtil);

    ReflectionUtils.setField(fieldReflectionUtil, handler, restTemplate);

    HttpHeaders headers = new HttpHeaders();
    headers.set("Content-Type", "application/json");
    List<String> payload = new ArrayList<>();

  when(restTemplate.exchange(Mockito.anyString(), Mockito.<HttpMethod> eq(HttpMethod.POST),
        Mockito.eq(new HttpEntity<>(payload.toString(), headers)), Mockito.<Class<Object>> any()).getBody())
            .thenReturn(Mockito.anyString());
}

问题根源

1. 错误的链式Mock调用

你现在试图直接Mock exchange().getBody()的返回值,但exchange()方法被Mock后默认返回null——所以调用getBody()时自然会触发空指针。正确的逻辑是:先让exchange()返回一个非null的ResponseEntity实例,再让这个实例的getBody()返回你想要的结果。

2. HttpEntity的匹配问题

你用Mockito.eq(new HttpEntity<>(...))来匹配请求实体,但HttpEntity的默认equals()是基于对象引用的,除非你重写了这个方法,否则这个匹配几乎不可能成功。这会导致你的Mock规则不生效,exchange()还是返回null。

3. 类型匹配错误

在MyService的handler()里,exchange的最后一个参数是String.class,但你的测试中用了Mockito.<Class<Object>> any()——类型不匹配会导致Mock规则失效,同样会让exchange()返回null。

修复后的测试代码

下面是修正后的测试类,解决了所有问题,而且代码更简洁:

class RestTemplateTest {

    @Mock
    private RestTemplate restTemplate;
    
    // 用@InjectMocks自动注入Mock的RestTemplate到MyService,省去手动反射
    @InjectMocks
    private MyService myService;

    @BeforeEach
    void setUp() {
        // 初始化所有Mock和被测试对象
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void handler_shouldRetrieveTokenSuccessfully() {
        // 准备预期的返回令牌
        String mockAuthToken = "fake-token-123";
        // 创建一个非null的ResponseEntity,设置返回体和状态码
        ResponseEntity<String> mockResponse = new ResponseEntity<>(mockAuthToken, HttpStatus.OK);

        // 正确Mock exchange方法:让它返回我们准备好的ResponseEntity
        when(restTemplate.exchange(
                anyString(), // 匹配任意URL
                eq(HttpMethod.POST), // 匹配POST方法
                any(HttpEntity.class), // 灵活匹配任意HttpEntity,避免equals问题
                eq(String.class) // 匹配正确的返回类型(String.class)
        )).thenReturn(mockResponse);

        // 执行被测试的方法
        myService.handler();

        // 可选:验证RestTemplate确实被调用了一次
        verify(restTemplate, times(1)).exchange(
                anyString(),
                eq(HttpMethod.POST),
                any(HttpEntity.class),
                eq(String.class)
        );
    }
}

额外优化建议

  • 优先用@InjectMocks替代手动反射:Mockito会自动帮你把Mock对象注入到被测试类中,代码更简洁,不易出错。
  • 避免对复杂对象用eq()匹配:对于HttpEntity这类包含headers和body的对象,用any()或者自定义argThat()匹配器会更灵活。
  • 明确Mock的返回对象:直接创建ResponseEntity实例,而不是试图链式Mock方法调用,逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:18:30