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

使用Mock Repository的单元测试失败问题排查

问题分析

报错的核心原因是你试图用Mockito的when()存根真实OffenceService实例的getById方法,这不符合Mockito的使用规则。真实对象的方法不能直接用when().thenReturn()来存根,而且实际上你完全不需要mockgetById——因为getById内部会调用你已经mock好的offenceEntityRepository.findById(),只要repository的mock正确,getById就能正常返回目标实体。

修复步骤
  1. 移除错误的mock语句
    删除测试代码中的这一行:

    Mockito.when(offenceService.getById(offenceID)).thenReturn(offenceEntityFromDB);
    

    因为offenceService是通过@Autowired注入的真实服务实例,Mockito的when()无法直接存根真实对象的方法,这会干扰内部逻辑的执行。

  2. 补充mockexistsByName方法
    你的测试场景中,请求的名称和原实体名称不同,代码会触发existsByName检查。需要mock这个方法返回false,避免抛出ValueAlreadyExistsException:

    Mockito.when(offenceEntityRepository.existsByName(offenceUpdateRequestModel.getName())).thenReturn(false);
    
  3. mocksaveAndFlush方法(可选但规范)
    虽然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());
}
额外注意事项
  • 如果你需要部分mockOffenceService(比如某些复杂方法),可以将@Autowired替换为@SpyBean,这样既能保留真实方法的逻辑,又能存根特定方法。但在这个场景下,完全不需要mockgetById,所以用@Autowired即可。
  • 确保测试类中的OffenceService和OffenceEntityRepository的注入方式正确:@Autowired注入真实服务,@MockBean注入mock的仓库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:33:26