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

如何为void方法编写JUnit测试用例?解决覆盖率为0问题

问题根源

你测试覆盖率为0的核心原因是在测试方法里自行mock了CustomerServiceImpl实例,而非使用@InjectMocks注解创建的真实待测试实例。mock对象的方法默认是空实现,不会执行真实业务代码,自然不会产生覆盖率。

正确的测试写法

先修正测试类的基础结构,移除不必要的mock,使用@InjectMocks注入的实例执行测试,同时补充完整的测试场景:

修正后的测试类

@ExtendWith(MockitoExtension.class) // 替代@Before中的初始化代码,更简洁
public class CustomerServiceImplTest {

    @InjectMocks
    private CustomerServiceImpl customerServiceImpl; // 待测试的真实业务实例

    @Mock
    private ERepository eRepository;

    @Mock
    private ActivityUtil activityUtil;

    // 构建测试用的RequestDTO
    private RequestDTO buildValidRequestDTO() {
        RequestDTO requestDTO = new RequestDTO();
        requestDTO.setAdmissionId(1L); // 必填字段,否则直接触发异常
        requestDTO.setEmail("test@example.com");
        requestDTO.setCustomerPhone("1234567890");
        requestDTO.setZipCode("100001");
        requestDTO.setAddress1("Test Address");
        return requestDTO;
    }

    // 测试场景1:正常更新成功
    @Test
    public void updateCustomer_Success() throws CustomAException {
        // 1. 准备测试数据
        RequestDTO requestDTO = buildValidRequestDTO();
        Admission admission = new Admission();
        Customer customer = new Customer();
        admission.setCustomer(customer);
        
        // 2. 模拟依赖行为
        when(eRepository.findById(requestDTO.getAdmissionId())).thenReturn(Optional.of(admission));
        doNothing().when(activityUtil).createActivityLog(any(Admission.class), eq(Constants.ENTRY_COMPLETED));

        // 3. 执行待测试方法
        customerServiceImpl.updateCustomer(requestDTO);

        // 4. 验证结果:检查属性更新、依赖方法调用
        assertEquals(requestDTO.getEmail(), customer.getCustomerEmailAddress());
        assertEquals(requestDTO.getCustomerPhone(), customer.getCorporateTelephoneNumber());
        verify(eRepository, times(1)).save(admission);
        verify(activityUtil, times(1)).createActivityLog(admission, Constants.ENTRY_COMPLETED);
    }

    // 测试场景2:AdmissionId为空,抛出异常
    @Test(expected = CustomAException.class)
    public void updateCustomer_AdmissionIdNull_ThrowsException() throws CustomAException {
        RequestDTO requestDTO = buildValidRequestDTO();
        requestDTO.setAdmissionId(null);

        customerServiceImpl.updateCustomer(requestDTO);
    }

    // 测试场景3:Admission不存在,抛出异常
    @Test(expected = CustomAException.class)
    public void updateCustomer_AdmissionNotFound_ThrowsException() throws CustomAException {
        RequestDTO requestDTO = buildValidRequestDTO();
        
        when(eRepository.findById(requestDTO.getAdmissionId())).thenReturn(Optional.empty());

        customerServiceImpl.updateCustomer(requestDTO);
    }

    // 测试场景4:Customer为空,抛出异常
    @Test(expected = CustomAException.class)
    public void updateCustomer_CustomerNotFound_ThrowsException() throws CustomAException {
        RequestDTO requestDTO = buildValidRequestDTO();
        Admission admission = new Admission();
        admission.setCustomer(null);
        
        when(eRepository.findById(requestDTO.getAdmissionId())).thenReturn(Optional.of(admission));

        customerServiceImpl.updateCustomer(requestDTO);
    }

    // 测试场景5:更新过程中触发异常,转换为CustomAException
    @Test(expected = CustomAException.class)
    public void updateCustomer_RepositoryError_ThrowsException() throws CustomAException {
        RequestDTO requestDTO = buildValidRequestDTO();
        Admission admission = new Admission();
        Customer customer = new Customer();
        admission.setCustomer(customer);
        
        when(eRepository.findById(requestDTO.getAdmissionId())).thenReturn(Optional.of(admission));
        when(eRepository.save(any(Admission.class))).thenThrow(new RuntimeException("DB Error"));

        customerServiceImpl.updateCustomer(requestDTO);
    }
}

关键修正点

  • 移除自行mock的Service实例:直接使用@InjectMocks注入的customerServiceImpl,这是真实的业务对象,会执行核心业务代码。
  • 简化初始化方式:用@ExtendWith(MockitoExtension.class)替代手动调用MockitoAnnotations.initMocks(this),更符合Mockito的现代用法。
  • 覆盖全量测试场景:包含正常流程、各类异常分支,确保代码覆盖率达标。
  • void方法测试核心:
    1. 验证依赖组件的方法(如eRepository.save、activityUtil.createActivityLog)是否按预期调用。
    2. 验证业务对象的状态(如Customer的邮箱、电话等属性)是否被正确修改。
    3. 验证异常是否按业务规则抛出。

额外注意事项

  • 测试用例中必须正确设置RequestDTO的admissionId,否则会直接触发“Admission id not found”的异常。
  • 若仅测试Service逻辑,无需使用@SpringBootTest(会启动完整Spring上下文,测试速度慢),用@ExtendWith(MockitoExtension.class)即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 05:03:37