SpringBootTest空指针异常:UserRepository为null导致方法调用失败
解决UserService测试中的NullPointerException问题
问题原因
你的测试类同时混用了Mockito单元测试注解和SpringBoot集成测试注解,导致依赖注入异常:
@SpringBootTest会启动完整的Spring上下文,但你又用了@ExtendWith(MockitoExtension.class)做Mock测试,两者冲突。- 用
@Autowired注入UserRepository和UserConverter在Mockito单元测试环境下不会生效,这些依赖实际是null,进而导致Service里的userRepository为null。 MockitoAnnotations.openMocks(this)在使用@ExtendWith(MockitoExtension.class)时属于冗余代码,该扩展会自动初始化Mock实例。
修正方案
1. 调整测试类注解与依赖注入方式
移除@SpringBootTest,改用@Mock模拟Service依赖,@InjectMocks自动将Mock注入到Service中:
@ExtendWith(MockitoExtension.class) public class UserServiceTest { // 用@Mock模拟依赖 @Mock private UserRepository userRepository; @Mock private UserConverter userConverter; // @InjectMocks自动将上面的Mock注入到userService中 @InjectMocks private UserService userService; @Test public void findByUsername200() throws Exception { String name = "luxubu@gmail.com"; UserEntity userEntity = new UserEntity(); userEntity.setUsername(name); // 模拟Repository的返回值 when(userRepository.findByUsername(name)).thenReturn(userEntity); UserDTO mockUserDTO = new UserDTO(); mockUserDTO.setUsername(name); // 模拟Converter的转换结果 when(userConverter.UserToUserDTO(userEntity)).thenReturn(mockUserDTO); UserDTO actualResult = userService.findByUsername(name); assertEquals(mockUserDTO.getUsername(), actualResult.getUsername()); // 验证Repository的方法是否被调用 verify(userRepository, times(1)).findByUsername(name); } }
2. 优化UserService的依赖注入方式(可选但推荐)
为了让Mockito的@InjectMocks更可靠,建议Service类使用构造函数注入替代字段注入:
@Service public class UserService { private final UserRepository userRepository; private final UserConverter userConverter; // 构造函数注入,Spring会自动装配依赖 public UserService(UserRepository userRepository, UserConverter userConverter) { this.userRepository = userRepository; this.userConverter = userConverter; } @Override public UserDTO findByUsername(String username) { UserEntity user = userRepository.findByUsername(username); if (user == null) throw new CustomException.NotFoundException("Không tìm thấy người dùng " + username); return userConverter.UserToUserDTO(user); } }
额外说明
- 如果确实需要做集成测试(比如要测试真实的数据库交互),则保留
@SpringBootTest,移除@ExtendWith(MockitoExtension.class)和@Mock、@InjectMocks,直接用@Autowired注入所有Bean,但这种场景下你需要准备测试数据或者用测试容器。 - 单元测试的核心是隔离依赖,所以用Mock模拟Repository和Converter是正确的做法,不需要启动真实的Spring上下文。
内容的提问来源于stack exchange,提问作者NqanVo
相关产品推荐
相关产品推荐

