要让单元测试覆盖所有 catch 块,需主动触发每种预期异常并验证其处理逻辑:先明确各 catch 对应的异常来源与触发条件,再通过 mock、边界输入等方式精准注入异常,最后断言返回值、状态变更、日志或新异常等具体行为。
要让单元测试覆盖所有 catch 块中的边界异常处理,核心是**主动触发每种预期异常,并验证其处理逻辑是否按设计执行**——不是等异常“自然发生”,而是通过可控手段(如模拟、参数构造、依赖替换)让代码走进每个 catch 分支。
先梳理被测方法中所有 try-catch 结构,逐个确认:
service.doSomething())、参数校验(如 Objects.requireNonNull())、还是 I/O/网络等外部依赖?catch 捕获,需检查异常继承关系,避免误判“未覆盖”。对依赖外部组件(数据库、HTTP 客户端、文件系统等)的 catch,用 Mockito、MockitoExtension 或 TestContainers 等工具,让依赖在特定调用时抛出目标异常:
NullPointerException,验证空指针的兜底逻辑;IOException,验证 IO 异常分支;很多 catch 实际来自方法内部的显式 throw,比如:
立即学习“Java免费学习笔记(深入)”;
if (id <= 0) throw new IllegalArgumentException("id must be positive");
这类无需 mock 外部依赖,只需传入边界值即可覆盖:
id = 0 或 id = -1 触发 IllegalArgumentException;null 字符串触发 NullPointerException(若未用 @NonNull 等注解提前拦截);覆盖 ≠ 执行了 catch 块,而是要证明它**正确执行了业务逻辑**。常见验证点:
Optional.empty() 或默认对象,用 assertThat(result).isEmpty() 断言;LogCaptor 或 mock logger,验证日志级别和关键信息;throw new ServiceException(e)),用 assertThrows<ServiceException>(...) 捕获并验证原因。不复杂但容易忽略:每个 catch 都对应一个明确的测试用例,命名清晰(如 shouldReturnEmptyWhenDatabaseThrowsSQLException),失败时能一眼定位哪条异常路径没走通。