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

Spring Boot Mockito中使用@InjectMocks仅Mock指定依赖的实现方法

问题分析与解决方案

问题背景

你有一个标注了@Service的UserServiceImpl类,依赖UserRepository(希望保留真实实例)和SessionService(需要Mock)。当前测试中,Mockito成功Mock了SessionService,但UserRepository却为null,导致调用save()方法时抛出空指针异常,你想知道是否可以只MockSessionService而保留真实的UserRepository,以及如何实现。

你的服务类代码:

@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private SessionService sessionService;

    @Override
    public User create(User user) {
        log.info("User Creation at Service");
        // ... 执行一些验证操作 ..
        // 该方法需要在单元测试中Mock
        String returnValue = sessionService.doSomethingThatIDontWantInTests();
        user.setInternalKey(returnValue);
        // .. 执行更多验证操作 ..
        return userRepository.save(user);
    }
}

测试类代码:

@SpringBootTest
class UserServiceTest {
    @InjectMocks
    private UserServiceImpl userService;
    @Mock
    private SessionService sessionService;

    @Test
    void CreateUserTest() {
        Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
        User user = new User();
        user.setName("John Doe");
        user.setEmail("john.doe@example.com");
        User savedUser = userService.create(user);
        assertNotNull(savedUser.getUserId());
    }
}

错误信息:

java.lang.NullPointerException: Cannot invoke "com.myproject.data.repos.UserRepository.save(User)" because "this.userRepository" is null


为什么原来的方法会失败?

你用的@InjectMocks是Mockito的注解,它的作用是创建被测试类的实例,并手动注入用@Mock/@Spy标注的依赖,但它完全不会和Spring容器交互。也就是说,UserRepository作为Spring管理的Bean,@InjectMocks根本不知道它的存在,自然不会注入,导致null。


解决方案:只Mock SessionService,保留真实UserRepository

完全可以实现你的需求,只需要改用Spring Boot提供的测试注解,让Spring容器帮你处理依赖注入:

方法1:使用@SpringBootTest + @MockBean + @Autowired

@MockBean是Spring Boot的核心测试注解,它会自动将Spring容器中对应的Bean替换成Mock实例,而其他依赖(比如UserRepository)会正常从容器中获取真实实例。修改后的测试类如下:

@SpringBootTest
class UserServiceTest {
    // 直接从Spring容器注入真实的UserServiceImpl实例
    @Autowired
    private UserServiceImpl userService;

    // 将Spring容器中的SessionService替换为Mock实例
    @MockBean
    private SessionService sessionService;

    @Test
    void CreateUserTest() {
        // 配置Mock的返回值
        Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
        
        User user = new User();
        user.setName("John Doe");
        user.setEmail("john.doe@example.com");
        
        User savedUser = userService.create(user);
        assertNotNull(savedUser.getUserId());
    }
}

方法2:使用轻量测试切片(Spring Boot 3.1+)

如果不想加载完整的Spring上下文(启动更快),可以用Spring Boot 3.1及以上版本提供的@ServiceTest切片注解,它只会加载服务层相关的Bean:

@ServiceTest
class UserServiceTest {
    @Autowired
    private UserServiceImpl userService;

    @MockBean
    private SessionService sessionService;

    @Test
    void CreateUserTest() {
        Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
        
        User user = new User();
        user.setName("John Doe");
        user.setEmail("john.doe@example.com");
        
        User savedUser = userService.create(user);
        assertNotNull(savedUser.getUserId());
    }
}

总结

你完全不需要MockUserRepository,通过Spring Boot的@MockBean注解,就能轻松实现只替换SessionService为Mock实例,同时保留UserRepository的真实实例。核心是让Spring容器管理依赖注入,而不是只依赖Mockito的@InjectMocks。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 02:43:14