Mockito单元测试问题:调用save()后Repository返回Null
解决Mockito中Repository.save()返回Null的问题
问题根源
核心问题是Mockito参数匹配不匹配:
- 测试代码中你mock的是
userDaoRepository.save(userToPassParam),指定返回预设的user对象 - 但在Service的
createUser方法中,会先修改传入的user对象的密码(user.setPassword(encodePassword)),再调用save(user) - 此时传入save的对象和mock时用的
userToPassParam既不是同一个实例,字段值(密码)也不一致,Mockito找不到对应的mock规则,因此返回默认的null
修复方案
方案1:使用参数匹配器匹配任意User对象
最简单的方式是用any(User.class)作为参数匹配器,不管传入什么User实例,都返回预设的user:
// 替换原测试中save方法的mock代码 Mockito.when(userDaoRepository.save(Mockito.any(User.class))).thenReturn(user);
方案2:精准匹配实际传入的对象
提前模拟Service中对密码的修改逻辑,确保mock的参数和实际传入的对象一致:
@Test void createUserNoExistValidConfirmation() throws Exception { User userToCallServiceImpl = new User(); userToCallServiceImpl.setUserName("Username A"); userToCallServiceImpl.setPassword("Pass A"); userToCallServiceImpl.setConfirmPassword("Pass A"); // 模拟密码加密结果 String encodedPassword = "Pass A"; Mockito.when(bCryptPasswordEncoder.encode(userToCallServiceImpl.getPassword())).thenReturn(encodedPassword); // 按业务逻辑mock用户名查询结果(若需验证用户名可用,应返回Optional.empty(),原测试逻辑可能需调整) Mockito.when(userDaoRepository.findByUserName(userToCallServiceImpl.getUserName())).thenReturn(Optional.ofNullable(user)); // 创建与Service中save前完全一致的对象 User modifiedUser = new User(); modifiedUser.setUserName(userToCallServiceImpl.getUserName()); modifiedUser.setPassword(encodedPassword); modifiedUser.setConfirmPassword(userToCallServiceImpl.getConfirmPassword()); // 匹配该对象 Mockito.when(userDaoRepository.save(Mockito.eq(modifiedUser))).thenReturn(user); User resultUser = userServiceImpl.createUser(userToCallServiceImpl); Assertions.assertEquals(user.getUserName(), resultUser.getUserName()); }
方案3:使用refEq忽略特定字段差异
如果User类的equals方法包含密码字段,可使用refEq匹配器忽略密码的变化:
// 替换原测试中save方法的mock代码,忽略password字段 Mockito.when(userDaoRepository.save(Mockito.refEq(userToCallServiceImpl, "password"))).thenReturn(user);
额外注意点
原测试中checkUserNameAvailable的mock逻辑可能存在业务矛盾:如果是验证用户名可注册,findByUserName应返回Optional.empty()(表示用户名未被占用),而非Optional.ofNullable(user)(表示用户名已存在)。若业务逻辑是“用户名不存在才允许创建”,此mock返回值会导致checkUserNameAvailable返回false,进而跳过save逻辑,最终也会返回null。
内容的提问来源于stack exchange,提问作者Naslo Flaquez
相关产品推荐
相关产品推荐

