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

如何用JUnit 5和Mockito测试含异常处理的服务层更新方法

问题解决思路与修正代码

核心问题分析

你当前的测试代码存在两个关键错误:

  1. 错误地对被测试的Service实例(syndromeTypeServiceMock,由@InjectMocks注入的真实实现)使用given(...)来Mock方法,这不符合Mockito的使用逻辑——我们应该Mock的是Service依赖的Repository,而非Service本身。
  2. 未对syndromeTypeTransRepository.findById()方法设置Mock行为,导致它返回空Optional,触发Service中的异常校验逻辑。

下面分两种场景给出修正方案:


场景1:测试正常修改流程(synTypeTransId存在)

需要Mock Repository的两个关键方法:

  • findById()返回存在的SyndromeTypeTransEntity实例,避免触发异常
  • save()返回构造好的实体,让Service能生成预期的返回结果

修正后的测试代码:

@ExtendWith(MockitoExtension.class)
public class SyndromeTypeServiceTest {

    Logger LOGGER = LoggerFactory.getLogger(getClass());
    
    @Mock
    SyndromeTypeRepository syndromeTypeRepository;
    @Mock
    SyndromeTypeTransRepository syndromeTypeTransRepository;
    @InjectMocks
    SyndromeTypeServiceImpl syndromeTypeService; // 改名更贴合真实实例
    
    @Test
    public void test_modify_SyndromeType_when_synTransId_exist() throws PersistenceException {
        LOGGER.info("test_modify_SyndromeType_when_synTransId_exist >>");
        
        // 1. 构造测试用的请求对象
        SyndromeType synTypeRequest = SyndromeType.builder()
                                                .synTypeId(10)
                                                .synTypeName("Musculoesquelético.")
                                                .lanCode("es")
                                                .synTypeTransId(10)
                                                .build();
        
        // 2. Mock Repository findById的返回值:构造存在的SyndromeTypeTransEntity
        SyndromeTypeTransEntity existingTransEntity = SyndromeTypeTransEntity.builder()
                .synTypeTransId(10)
                .synTypeEtt(SyndromeTypeEntity.builder().synTypeId(10).build())
                .lanCodeEtt(LanguageEntity.builder().lanCode("es").build())
                .synTypeName("Original Name")
                .build();
        BDDMockito.given(syndromeTypeTransRepository.findById(10)).willReturn(Optional.of(existingTransEntity));
        
        // 3. Mock Repository save的返回值:构造保存后的实体
        SyndromeTypeTransEntity savedTransEntity = SyndromeTypeTransEntity.builder()
                .synTypeTransId(10)
                .synTypeEtt(SyndromeTypeEntity.builder().synTypeId(10).build())
                .lanCodeEtt(LanguageEntity.builder().lanCode("es").build())
                .synTypeName("Musculoesquelético.")
                .build();
        BDDMockito.given(syndromeTypeTransRepository.save(BDDMockito.any(SyndromeTypeTransEntity.class))).willReturn(savedTransEntity);
        
        // 4. 调用真实的Service方法
        SyndromeType actualResult = syndromeTypeService.modifySyndromeType(synTypeRequest);
        
        // 5. 断言结果符合预期
        assertNotNull(actualResult);
        assertEquals(synTypeRequest.getSynTypeTransId(), actualResult.getSynTypeTransId());
        assertEquals(synTypeRequest.getSynTypeId(), actualResult.getSynTypeId());
        assertEquals(synTypeRequest.getSynTypeName(), actualResult.getSynTypeName());
        assertEquals(synTypeRequest.getLanCode(), actualResult.getLanCode());

        LOGGER.info("<< test_modify_SyndromeType_when_synTransId_exist");
    }
}

场景2:测试异常流程(synTypeTransId不存在,抛出PersistenceException)

需要让findById()返回空Optional,然后验证Service是否正确抛出异常。

测试代码:

@Test
public void test_modify_SyndromeType_when_synTransId_not_exist() {
    LOGGER.info("test_modify_SyndromeType_when_synTransId_not_exist >>");
    
    // 构造请求对象,传入不存在的synTypeTransId
    SyndromeType synTypeRequest = SyndromeType.builder()
                                            .synTypeId(999)
                                            .synTypeName("Test")
                                            .lanCode("es")
                                            .synTypeTransId(999)
                                            .build();
    
    // Mock findById返回空Optional
    BDDMockito.given(syndromeTypeTransRepository.findById(999)).willReturn(Optional.empty());
    
    // 验证调用Service方法时抛出指定异常
    PersistenceException exception = assertThrows(PersistenceException.class, 
            () -> syndromeTypeService.modifySyndromeType(synTypeRequest));
    
    // 可选:断言异常信息符合预期
    assertEquals(ErrorMessage.RECORD_NOT_EXIST.name(), exception.getMessage());

    LOGGER.info("<< test_modify_SyndromeType_when_synTransId_not_exist");
}

关键要点总结

  • 永远不要Mock被测试的类本身:@InjectMocks注入的是真实的Service实例,我们只需要Mock它的依赖(Repository)来控制执行流程。
  • 精准Mock依赖方法的行为:根据测试场景,设置Repository方法返回预期值(存在的实体或空Optional),让Service按我们设计的路径执行。
  • 异常验证用assertThrows:JUnit 5的assertThrows可以直接捕获并验证方法抛出的异常类型和信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:12:05