使用Mock Repository的单元测试失败问题排查
问题分析
报错的核心原因是你试图用Mockito的when()存根真实OffenceService实例的getById方法,这不符合Mockito的使用规则。真实对象的方法不能直接用when().thenReturn()来存根,而且实际上你完全不需要mockgetById——因为getById内部会调用你已经mock好的offenceEntityRepository.findById(),只要repository的mock正确,getById就能正常返回目标实体。
修复步骤
移除错误的mock语句
删除测试代码中的这一行:Mockito.when(offenceService.getById(offenceID)).thenReturn(offenceEntityFromDB);因为
offenceService是通过@Autowired注入的真实服务实例,Mockito的when()无法直接存根真实对象的方法,这会干扰内部逻辑的执行。补充mock
existsByName方法
你的测试场景中,请求的名称和原实体名称不同,代码会触发existsByName检查。需要mock这个方法返回false,避免抛出ValueAlreadyExistsException:Mockito.when(offenceEntityRepository.existsByName(offenceUpdateRequestModel.getName())).thenReturn(false);mock
saveAndFlush方法(可选但规范)
虽然BeanUtils.copyProperties已经修改了原实体,但为了单元测试的完整性,建议mocksaveAndFlush返回修改后的实体:Mockito.when(offenceEntityRepository.saveAndFlush(offenceEntityFromDB)).thenReturn(offenceEntityFromDB);
修复后的完整测试代码
@Test public void putWorks() { //given String offenceID = "00000000000000000000000000000002"; OffenceUpdateRequestModel offenceUpdateRequestModel = new OffenceUpdateRequestModel( "Speed limit 40km/h", 8, 8000); OffenceEntity offenceEntityFromDB = new OffenceEntity("00000000000000000000000000000002", "Speed limit 20km/h", 4, 4000, Set.of(), true); // Mock repository的findById,返回目标实体的Optional Mockito.when(offenceEntityRepository.findById(offenceID)).thenReturn(Optional.of(offenceEntityFromDB)); // Mock existsByName,确保新名称不存在 Mockito.when(offenceEntityRepository.existsByName(offenceUpdateRequestModel.getName())).thenReturn(false); // Mock saveAndFlush,返回更新后的实体 Mockito.when(offenceEntityRepository.saveAndFlush(offenceEntityFromDB)).thenReturn(offenceEntityFromDB); //when OffenceEntity result = offenceService.put(offenceID, offenceUpdateRequestModel); //then assertEquals(result.getName(), offenceUpdateRequestModel.getName()); assertEquals(result.getAmountOfFine(), offenceUpdateRequestModel.getAmountOfFine(), 0.0); assertEquals(result.getPenaltyPoints(), offenceUpdateRequestModel.getPenaltyPoints()); }
额外注意事项
- 如果你需要部分mock
OffenceService(比如某些复杂方法),可以将@Autowired替换为@SpyBean,这样既能保留真实方法的逻辑,又能存根特定方法。但在这个场景下,完全不需要mockgetById,所以用@Autowired即可。 - 确保测试类中的
OffenceService和OffenceEntityRepository的注入方式正确:@Autowired注入真实服务,@MockBean注入mock的仓库。
内容的提问来源于stack exchange,提问作者Lulex97
相关产品推荐
相关产品推荐

