如何在Mockito创建的Mock对象中注入Spring Boot真实Bean?
解决Mock UserService时注入真实UserRepository的问题
这是个很典型的Mockito使用误区——你混淆了完全Mock和**部分Mock(Spy)**的场景,我来帮你理清思路并解决问题:
问题本质
你用@Mock创建的UserService是一个完全模拟的假对象,所有方法默认都是空实现,Spring不会把真实的UserRepository注入进去(因为mock对象根本不是Spring容器管理的Bean)。而@InjectMocks只会把其他@Mock标记的对象注入进去,不会自动关联真实的Bean,这就是为什么你会遇到空指针异常。
正确方案:使用Mockito Spy(部分Mock)
你真正需要的是保留UserService的真实行为,仅跳过validateUser和createToken这两个方法,这时候应该用Mockito的Spy来包装真实的UserService实例——Spy会基于真实对象创建代理,既保留原对象的依赖(已经被Spring注入好的UserRepository),又允许你mock特定方法。
实现方式1:手动创建Spy(基础写法)
@SpringBootTest public class UserServiceTest { // 让Spring注入真实的UserService(里面已经包含真实的UserRepository) @Autowired private UserService realUserService; @Test public void testUpdateUser() { // 基于真实对象创建Spy UserService spyUserService = Mockito.spy(realUserService); // 跳过validateUser和createToken方法 Mockito.doNothing().when(spyUserService).validateUser(Mockito.any()); Mockito.doNothing().when(spyUserService).createToken(Mockito.any()); // 调用spy对象的updateUser,此时内部的UserRepository是真实的 spyUserService.updateUser(yourUserParam); // 后续断言逻辑 } }
实现方式2:用Spring Boot的@SpyBean注解(更简洁)
Spring Boot提供了@SpyBean注解,可以直接把Spring容器里的真实Bean包装成Spy,不需要手动创建:
@SpringBootTest public class UserServiceTest { // 自动把容器里的真实UserService包装成Spy @SpyBean private UserService userService; @Autowired private UserRepository userRepository; // 可选,如果你需要直接操作Repository做断言 @Test public void testUpdateUser() { // 跳过指定方法 Mockito.doNothing().when(userService).validateUser(Mockito.any()); Mockito.doNothing().when(userService).createToken(Mockito.any()); // 调用updateUser,内部UserRepository是真实的 userService.updateUser(yourUserParam); // 比如断言Repository里的数据是否正确更新 User updatedUser = userRepository.findById(yourUserId).orElseThrow(); Assertions.assertEquals(yourExpectedValue, updatedUser.getSomeField()); } }
关键注意事项
- 不要把
@Mock和@SpyBean混用在同一个类上,避免依赖混乱 - 如果你只需要测试Service层和Repository的交互,也可以用
@DataJpaTest配合@Import(UserService.class)来缩小上下文范围,提升测试速度 - 用
doNothing()的时候,要注意方法的返回值:如果validateUser或createToken有返回值,可能需要用doReturn()代替,比如doReturn(true).when(spyUserService).validateUser(Mockito.any())
内容的提问来源于stack exchange,提问作者pizza.addict
相关产品推荐
相关产品推荐

