核心是用assertThrows捕获异常实例并精准断言:传入自定义异常Class和Lambda待测逻辑,返回异常对象后校验getMessage()、getErrorCode()等字段,避免try-catch或@Test(expected=...)的局限性。

Java 中验证自定义异常是否被正确抛出,核心是用 assertThrows 捕获异常实例并做精准断言,避免传统 try-catch 或 @Test(expected = ...) 的局限性。
用 assertThrows 断言异常类型和消息
JUnit 5 的 assertThrows 接收异常类和一段可执行逻辑(Lambda),返回实际抛出的异常对象,可直接校验其内容:
- 传入自定义异常的 Class,如
InsufficientBalanceException.class - Lambda 中调用待测方法,确保该路径确实会触发异常
- 对返回的异常对象调用
getMessage()、getErrorCode()等 getter 方法做断言
示例:
InsufficientBalanceException e = assertThrows(InsufficientBalanceException.class, () -> account.withdraw(200.0));
assertEquals("余额不足:200.0", e.getMessage());
assertEquals("ERR_BALANCE", e.getErrorCode());
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
立即学习“Java免费学习笔记(深入)”;
区分运行时与检查异常的 mock 处理
若需在测试中模拟服务层抛出自定义异常(比如用 Mockito),要注意异常类型是否匹配方法签名:
- 继承
RuntimeException的自定义异常:可直接用doThrow(...).when(...).method() - 继承
Exception的检查异常:被 mock 的方法签名中必须声明throws YourCheckedException,否则编译失败 - 对 void 方法,只能用
doThrow开头;对有返回值方法,when(...).thenThrow(...)也可用,但语义不如doThrow统一
结合业务逻辑验证异常影响
真实场景中,异常往往触发降级、码值封装或事务回滚。测试不能只看“有没有抛”,还要看“抛得对不对”:
- 如果业务方法 catch 并包装为统一响应,就验证返回对象的
code、message字段 - 如果异常应导致数据库操作不生效,可检查状态变更未发生(如余额未扣减)
- 避免只断言异常非 null 或仅依赖
@Test(expected = ...),它无法定位异常发生位置,也拿不到异常实例
确保测试环境准备就绪
几个易忽略但关键的前提:
- Maven 依赖必须是 JUnit 5(
junit-jupiter),不是 JUnit 4 - 自定义异常类要提供带参构造器,并公开必要的字段访问方法(如
getErrorCode()) - 被测代码中抛异常的分支必须能被执行到——必要时配合 Mockito 打桩控制输入或外部依赖行为

















