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

测试无返回值void方法遇Mockito报错且无代码覆盖率的问题

测试void方法获取代码覆盖率失败的问题与修复

待测试的VaultService方法

@Service
public class VaultService {

    private final ProductRepository repository;

    private final DatabaseService databaseService;

    public VaultService(
            @Autowired BuildProperties buildProperties,
            ProductRepository repository,
            DatabaseService databaseService
    ) {
        logger.info("Creating service");

        this.repository = repository;
        this.databaseService = databaseService;
    }

    protected void checkVaultResponseErrors(VaultResponse vaultResponse) {
        if (vaultResponse.responseStatus != ResponseStatus.SUCCESS) {
            vaultResponse.errors.forEach(error -> {
                if (vaultResponse.responseStatus.equals(ResponseStatus.EXCEPTION)) {
                    throw new VaultException(error.getMessage());
                }
                if (vaultResponse.responseStatus.equals(ResponseStatus.FAILURE)) {
                    throw new VaultFailure(error.getMessage());
                }
            });
        }
    }
}

VaultResponse类定义

@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class VaultResponse {
    @JsonProperty("responseStatus")
    public ResponseStatus responseStatus;

    @JsonProperty("errors")
    public List<Error> errors;
}

当前错误的测试代码

@Test
void should_check_vaultResponse_for_errors() {
    VaultService spyService = Mockito.spy(vaultService);
    VaultResponse vaultResponse = new VaultResponse();
    ResponseStatus status = ResponseStatus.SUCCESS;
    List<Error> error = new ArrayList<>();

    vaultResponse.setResponseStatus(status);
    vaultResponse.setErrors(error);

    doThrow(IllegalArgumentException.class)
            .when(spyService).checkVaultResponseErrors(vaultResponse);
    doNothing().when(spyService).checkVaultResponseErrors(vaultResponse);

    verify(spyService, times(1)).checkVaultResponseErrors(vaultResponse);
}

问题分析

  • 未执行真实方法:测试仅设置Mock行为,未调用spyService.checkVaultResponseErrors(vaultResponse),真实逻辑完全没运行,自然无代码覆盖率。
  • Mock行为冲突:先设置doThrow再设置doNothing,后者覆盖前者,重复设置无意义。
  • Spy使用错误:Spy的作用是调用真实方法,却用doNothing跳过逻辑,违背测试初衷。
  • 测试场景单一:仅覆盖SUCCESS状态,未覆盖EXCEPTION和FAILURE分支,无法获取完整覆盖率。

修正后的测试代码

针对不同逻辑分支编写独立测试用例,确保每个分支都被覆盖:

测试1:SUCCESS状态无异常抛出

@Test
void should_not_throw_exception_when_response_status_is_success() {
    // 初始化被测试类,依赖用Mock替代
    VaultService vaultService = new VaultService(
        Mockito.mock(BuildProperties.class),
        Mockito.mock(ProductRepository.class),
        Mockito.mock(DatabaseService.class)
    );
    VaultResponse vaultResponse = new VaultResponse();
    vaultResponse.setResponseStatus(ResponseStatus.SUCCESS);
    vaultResponse.setErrors(new ArrayList<>());

    // 调用真实方法,无异常则测试通过
    vaultService.checkVaultResponseErrors(vaultResponse);
}

测试2:EXCEPTION状态抛出VaultException

@Test
void should_throw_vault_exception_when_response_status_is_exception() {
    VaultService vaultService = new VaultService(
        Mockito.mock(BuildProperties.class),
        Mockito.mock(ProductRepository.class),
        Mockito.mock(DatabaseService.class)
    );
    VaultResponse vaultResponse = new VaultResponse();
    vaultResponse.setResponseStatus(ResponseStatus.EXCEPTION);
    
    Error testError = new Error();
    testError.setMessage("Test exception error");
    vaultResponse.setErrors(List.of(testError));

    // 验证抛出指定异常及消息
    VaultException exception = Assertions.assertThrows(VaultException.class, 
        () -> vaultService.checkVaultResponseErrors(vaultResponse));
    Assertions.assertEquals("Test exception error", exception.getMessage());
}

测试3:FAILURE状态抛出VaultFailure

@Test
void should_throw_vault_failure_when_response_status_is_failure() {
    VaultService vaultService = new VaultService(
        Mockito.mock(BuildProperties.class),
        Mockito.mock(ProductRepository.class),
        Mockito.mock(DatabaseService.class)
    );
    VaultResponse vaultResponse = new VaultResponse();
    vaultResponse.setResponseStatus(ResponseStatus.FAILURE);
    
    Error testError = new Error();
    testError.setMessage("Test failure error");
    vaultResponse.setErrors(List.of(testError));

    // 验证抛出指定异常及消息
    VaultFailure failure = Assertions.assertThrows(VaultFailure.class, 
        () -> vaultService.checkVaultResponseErrors(vaultResponse));
    Assertions.assertEquals("Test failure error", failure.getMessage());
}

关键说明

  • 直接实例化被测试类:无需使用Spy,直接创建VaultService实例(依赖用Mockito Mock),调用真实方法才能覆盖代码逻辑。
  • 分场景测试:每个用例仅覆盖一个分支,逻辑清晰,便于定位问题。
  • 验证异常行为:针对非SUCCESS状态,通过assertThrows验证异常类型和消息是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:35:42