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

Spring Boot测试中UserRepository无法正常工作的问题排查

问题根源与解决方案

核心问题:存根(mock)覆盖了真实的保存逻辑

你在@BeforeEach中对userRepo.save()做了硬编码存根:

Mockito.when(userRepo.save(Mockito.any(UserEntity.class))).thenReturn(new UserEntity());

这段代码强制save()方法返回一个空的UserEntity实例,完全跳过了Spring Data JPA的真实保存逻辑——既不会把数据写入数据库,返回的对象自然全是null。

同时你用了@Spy修饰UserRepo,存根会覆盖Spy的默认行为(原本Spy会优先调用真实方法),导致真实的数据库操作完全没执行。


分场景解决方案

场景1:只想做单元测试(不操作真实数据库)

如果只是测试UserService的逻辑,不需要真的写库:

  1. 替换测试类注解:去掉@SpringBootTest,用@ExtendWith(MockitoExtension.class)简化Mock环境
  2. 修改存根逻辑,返回带字段的实体,匹配传入的参数:
    @BeforeEach
    public void setupMock() {
        // 让save返回传入的实体(模拟保存成功并返回带数据的对象)
        Mockito.when(userRepo.save(Mockito.any(UserEntity.class)))
                .thenAnswer(invocation -> {
                    UserEntity input = invocation.getArgument(0);
                    // 手动设置ID模拟数据库自增
                    input.setId(1L);
                    return input;
                });
        // 存根findByPhoneNumber,返回对应实体
        Mockito.when(userRepo.findByPhoneNumber(Mockito.anyString()))
                .thenAnswer(invocation -> {
                    String phone = invocation.getArgument(0);
                    Optional<UserEntity> user = getUserAddModels().stream()
                            .filter(m -> m.getPhoneNumber().equals(phone))
                            .map(m -> {
                                UserEntity entity = new UserEntity();
                                entity.setId(1L);
                                entity.setFirstName(m.getFirstName());
                                entity.setLastName(m.getLastName());
                                entity.setPatronymic(m.getPatronymic());
                                entity.setPhoneNumber(m.getPhoneNumber());
                                entity.setEmail(m.getEmail());
                                return entity;
                            })
                            .findFirst();
                    return user;
                });
    }
    
  3. 删除@AfterEach里的删库逻辑,因为未操作真实数据库。

场景2:做集成测试(操作真实数据库)

如果要验证真实的数据库保存逻辑:

  1. 去掉@InjectMocks、@Spy注解,改用@Autowired注入真实Bean:
    @SpringBootTest
    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    public class UserServiceTest {
    
        @Autowired
        private UserService userService;
    
        @Autowired
        private UserRepo userRepo;
    
        @Autowired
        private ValidationService validationService;
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        // 保留@AfterEach删库逻辑,避免测试数据污染
        @AfterEach
        public void deleteDb() {
            userRepo.deleteAll();
        }
    
        // 删除@BeforeEach里的存根代码,让Spring Data JPA执行真实save逻辑
        @BeforeEach
        public void setupMock() {}
    
        // 测试方法保持不变
        @Test
        @Order(1)
        public void testUserAdding() {
            // ... 原有代码
        }
    }
    
  2. 确保测试用数据库配置正确(比如用H2内存库,避免影响生产数据)。

额外排查点

  • 检查UserEntity注解:确认@Entity、@Id、@GeneratedValue等注解配置正确,保证JPA能识别实体和主键生成策略。
  • 检查UserRepo继承关系:确认继承了JpaRepository或CrudRepository,确保Spring Data JPA能生成正确的实现类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 01:53:14