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

