Mockito模拟RestTemplate单元测试返回null触发NPE问题排查
问题核心原因
- 测试框架注解冲突,两套体系不兼容
你在JUnit4测试类上同时标注了@RunWith(MockitoJUnitRunner.class)和@SpringBootTest。JUnit4的运行逻辑完全以@RunWith指定的Runner为准,MockitoJUnitRunner会全程接管测试流程,根本不会触发Spring相关的测试初始化逻辑——哪怕写了@SpringBootTest,也不会加载任何Spring上下文,整个测试本质是纯Mockito单元测试。 - 对Mock对象缺失必要的方法打桩,导致执行流提前中断
你只通过@Mock生成了EmailForwardConfig的Mock实例并注入到Controller中,但从来没有给这个Mock的getUrl()方法配置返回规则。Mockito对未打桩的方法默认返回空值:String类型返回null,引用类型返回null。
这也是为什么Mockito会提示restTemplate零交互的原因:代码在执行到emailConfig.getUrl()这步时,要么因为emailConfig未注入(也就是你说的移除@Mock后对象为null的场景)触发NPE,要么执行流因为其他配置问题在这里中断,根本走不到调用restTemplate.postForEntity()的环节,自然不会和这个Mock对象产生任何交互,你看到的ResponseEntity为null的现象,本质是代码没走到那一步的表现,不是RestTemplate Mock返回了null。 - 打桩和断言逻辑不匹配,全流程跑通后也会测试失败
你给RestTemplate打桩返回的响应体是小写字符串"done",但测试断言里校验的是大写的"Done",就算全流程正常执行,这个字符串相等断言也会失败。
修正操作步骤
- 明确测试类型,消除框架冲突
根据你要写的测试类型二选一,不要混用注解:- 纯单元测试场景:移除
@SpringBootTest注解,保留MockitoJUnitRunner,所有依赖通过Mockito的@Mock、@InjectMocks完成模拟 - Spring集成测试场景:把测试运行器替换为
SpringRunner.class,使用@MockBean注解替换Spring容器中的RestTemplate、EmailForwardConfigBean,不能用普通的@Mock
- 纯单元测试场景:移除
- 补全缺失的打桩逻辑
在测试方法或者@Before的setup方法中,给EmailForwardConfig的getUrl方法配置固定返回值,比如:
这一步会避免未打桩返回null导致的执行中断。Mockito.when(emailConfig.getUrl()).thenReturn("http://test-mail-server/"); - 统一打桩和断言的匹配规则
校准打桩的返回值和断言的校验值,比如把两边的字符串都统一为"done",避免大小写不匹配导致的断言失败。 - 确认打桩的执行顺序
所有Mockito的打桩逻辑,必须放在调用Controller接口方法之前执行,不能颠倒顺序。
按照上面的步骤调整后,再运行测试,就能看到RestTemplate的Mock对象正常被调用,不会再出现ResponseEntity为null的空指针问题。
内容的提问来源于stack exchange,提问作者kfersch
相关产品推荐
相关产品推荐

