Mockito 3.6.28调用doCallRealMethod抛出NullPointerException问题求助
问题根因
- Mockito 的
@InjectMocks注解只会为被它直接修饰的对象注入测试类中声明的@Mock依赖:你代码里仅ClassA实例会被自动注入LoggingService和ClassB的Mock实例。 - 你声明的
@Mock ClassB classB本身是Mockito生成的代理对象,@InjectMocks不会为这个Mock代理对象自动注入它自身需要的LoggingService依赖。当你通过doCallRealMethod()调用ClassB的真实业务方法时,ClassB实例内部的loggingService仍为null,因此抛出空指针异常。
可行解决方案
方案1:手动为Mock的ClassB注入依赖
可以借助Spring Test提供的ReflectionTestUtils快速赋值,也可以用Java原生反射实现,示例如下:
@RunWith(MockitoJUnitRunner.Silent.class) public class TestClass{ @InjectMocks ClassA classA; @Mock ClassB classB; @Mock private LoggingService loggingService; @Before public void initDependency() { // 手动给ClassB的mock实例注入loggingService ReflectionTestUtils.setField(classB, "loggingService", loggingService); } @Test public void testMethod(){ doCallRealMethod().when(classB).doSomething(); classA.doSomething(); } }
方案2:用Spy替代Mock来持有真实ClassB实例
如果不需要MockClassB的其他方法,直接用@Spy修饰真实的ClassB实例,配合@InjectMocks(Mockito 3.3+版本支持)自动为其注入依赖:
@RunWith(MockitoJUnitRunner.Silent.class) public class TestClass{ @InjectMocks ClassA classA; @Spy @InjectMocks ClassB classB = new ClassB(); @Mock private LoggingService loggingService; @Test public void testMethod(){ // Spy默认调用真实方法,无需额外配置doCallRealMethod classA.doSomething(); } }
方案3:重构业务代码使用构造注入(推荐)
放弃字段注入改用构造注入,从根源上避免测试时依赖注入失效的问题,同时代码可维护性更高:
// 重构后的ClassB public class ClassB{ private final LoggingService loggingService; @Autowired public ClassB(LoggingService loggingService) { this.loggingService = loggingService; } public void doSomething(){ loggingService.info("info log"); } } // 重构后的ClassA public class ClassA{ private final LoggingService loggingService; private final ClassB classB; @Autowired public ClassA(LoggingService loggingService, ClassB classB) { this.loggingService = loggingService; this.classB = classB; } public void doSomething(){ loggingService.info("info log"); classB.doSomething(); } }
重构后原有测试代码无需调整,Mockito会自动识别构造方法参数,为所有实例注入对应的Mock依赖。
内容的提问来源于stack exchange,提问作者Ansar Samad
相关产品推荐
相关产品推荐

