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

使用Mockito thenCallRealMethod时遭遇NullPointerException求助

解决Mockito thenCallRealMethod调用ObjectMapper.readValue时的NullPointerException问题

这个问题我之前帮不少开发者排查过,核心原因其实和Mockito mock对象的本质有关——你用@Mock创建的ObjectMapper实例并不是一个真正通过构造流程初始化的对象,而是Mockito动态生成的代理类。这个代理类只是实现了ObjectMapper的接口方法,但内部所有的私有成员变量(比如负责反序列化的上下文、工厂实例这些核心组件)都是null。当你通过thenCallRealMethod()让这个mock对象调用真实的readValue方法时,方法内部会尝试访问这些未初始化的成员,自然就抛出NullPointerException了,而你自己的realObjectMapper是正常构造的,所有依赖都初始化完毕,所以能正常执行。

那该怎么解决呢?给你两个靠谱的方案,优先选第一个,完全符合你想要兼容未来版本的需求:

方案一:用Spy代替Mock创建ObjectMapper

Spy是Mockito提供的另一种测试替身,它基于真实的对象实例创建,会保留原对象的所有初始化状态和行为,同时允许你按需stub特定方法。这完美适配你的场景:

修改你的测试代码,把ObjectMapper的mock换成Spy:

// 替换原来的@Mock注解
@Spy
private ObjectMapper objectMapper = new ObjectMapper();

@Test
public void testMethod() { // 建议方法名首字母小写,符合Java规范
    // 正常stub restTemplate的方法
    when(restTemplate.postForEntity(...))
        .thenReturn(new ResponseEntity<>(realObjectMapper.writeValueAsString(data), HttpStatus.OK));
    
    // 因为Spy默认会调用真实方法,这里甚至不需要写thenCallRealMethod()
    // 如果确实需要显式指定(比如之前stub过readValue现在要改回来),也可以写:
    // doCallRealMethod().when(objectMapper).readValue(anyString(), eq(TestData.class));
    
    TestData result = tested.callMethod(...);
    // 你的断言逻辑...
}

这个方案的优势很明显:既保留了ObjectMapper的真实行为,又允许你stub其他需要模拟的方法,而且完全依赖ObjectMapper的公开API,不会耦合内部实现,完美契合你作为库要兼容未来版本的目标。

方案二:手动初始化Mock对象的内部依赖(不推荐)

如果你一定要用mock对象,理论上可以通过反射设置ObjectMapper内部的私有成员变量,但这非常不推荐:一来ObjectMapper的内部实现可能随版本变化,你的测试代码很容易在Jackson升级后失效;二来反射操作私有成员本身就破坏了封装性,维护成本极高,完全违背你想要兼容未来版本的初衷。所以除非万不得已,别选这个方案。

最后再提醒一句:对于像ObjectMapper这种依赖大量内部状态的工具类,尽量不要用Mock,优先用Spy或者直接使用真实实例,这样能避免很多莫名其妙的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:59:14