如何验证Mock服务中抽象父类方法调用并解决空指针异常?
问题根因
你当前使用Mockito.mock(MyService.class, InvocationOnMock::callRealMethod)生成的是纯Mock对象,Mock实例的所有字段默认都是null,即使指定了默认调用真实方法,Spring的@Autowired注入逻辑也不会自动触发,所以调用到userRepository的时候就会抛出空指针异常。
正确实现方案
方案1:使用Spy(部分Mock)实现(推荐)
Spy是Mockito专门为「部分方法走真实逻辑、部分方法可桩可验证」的场景设计的,默认会保留实例的原有依赖,比纯Mock加默认真实调用的方式更符合你的需求。
实现步骤:
- 定义Mock的UserRepository实例,提前桩好查询方法的返回值
- 构造MyService的真实实例,注入Mock的UserRepository后用
Mockito.spy()包装为Spy对象 - 调用测试方法后直接验证hello方法的调用次数
代码示例:
import org.mockito.Mock; import org.mockito.Spy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; public class MyServiceTest { // 声明要模拟的依赖 @Mock private UserRepository userRepository; // 声明Spy对象,默认所有方法走真实逻辑 @Spy private MyService myService; @BeforeEach void init() { // 初始化Mock和Spy注解 MockitoAnnotations.openMocks(this); // 手动把Mock的UserRepository注入到MyService中,如果你的Service有setter方法也可以直接调用setter ReflectionTestUtils.setField(myService, "userRepository", userRepository); } @Test void testWhateverShouldTriggerHello() { // 提前桩写UserRepository的查询返回 User mockUser = new User(); when(userRepository.selectById(any())).thenReturn(mockUser); // 构造测试入参 MyDTO myDTO = new MyDTO(); // 调用测试方法 myService.whatever(myDTO); // 验证父类hello方法被调用1次 verify(myService, times(1)).hello(any()); } }
方案2:沿用当前Mock+callRealMethod的方式
如果你不想修改现有的Mock创建逻辑,只需要手动给Mock对象的userRepository字段注入Mock的UserRepository实例即可,和方案1的注入逻辑一致:
MyService myService = Mockito.mock(MyService.class, InvocationOnMock::callRealMethod); UserRepository mockUserRepo = mock(UserRepository.class); // 注入依赖 ReflectionTestUtils.setField(myService, "userRepository", mockUserRepo); // 后续桩写和调用逻辑和方案1一致
注意事项
- 如果你的MyService使用构造器注入依赖,不需要用反射工具注入,直接
new MyService(mockUserRepository)后再包装为Spy对象即可,代码更安全。 - 不要对有Spring注入依赖的类直接使用纯Mock做真实方法调用,Spy是这类场景的最优选择。
内容的提问来源于stack exchange,提问作者Ersch
相关产品推荐
相关产品推荐

