如何为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方法测试核心:
- 验证依赖组件的方法(如
eRepository.save、activityUtil.createActivityLog)是否按预期调用。 - 验证业务对象的状态(如Customer的邮箱、电话等属性)是否被正确修改。
- 验证异常是否按业务规则抛出。
- 验证依赖组件的方法(如
额外注意事项
- 测试用例中必须正确设置
RequestDTO的admissionId,否则会直接触发“Admission id not found”的异常。 - 若仅测试Service逻辑,无需使用
@SpringBootTest(会启动完整Spring上下文,测试速度慢),用@ExtendWith(MockitoExtension.class)即可。
内容的提问来源于stack exchange,提问作者Thejus32
相关产品推荐
相关产品推荐

