AssertJ 的 assertThatThrownBy 用于校验业务异常,需用 lambda 延迟执行被测方法(如 () -> service.process(-1)),再链式调用 isInstanceOf() 和 hasMessage() 等精准断言类型与消息。

在单元测试中用 AssertJ 的 assertThatThrownBy 校验 Java 中 throw 抛出的业务异常,关键在于确保被测代码确实执行了抛出逻辑,并让 AssertJ 捕获到该异常实例。
确保 throw 语句实际被执行
常见错误是测试方法调用未触发异常路径(比如参数合法、条件未满足)。业务异常通常由 if 判断 + throw new XxxException(...) 构成,测试时需传入能触发该分支的输入。
- 检查被测方法中 throw 所在的条件是否可被外部控制(如校验空值、越界、状态非法等)
- 例如:若方法在
userId <= 0时 throwIllegalArgumentException,测试就该传入0或负数 - 避免因 mock 或逻辑跳过导致 throw 语句根本没运行
使用 assertThatThrownBy 包裹函数式调用
assertThatThrownBy 接收一个 ThrowableAssert.ThrowingCallable(即无参 lambda),它会在断言中执行并捕获异常。不能直接传方法调用结果(那会先抛出异常中断测试),必须包裹成延迟执行的表达式。
- ✅ 正确写法:
assertThatThrownBy(() -> service.process(-1)) - ❌ 错误写法:
assertThatThrownBy(service.process(-1))(此时异常已抛出,测试提前失败) - 如果方法有参数,确保 lambda 内完整调用,包括所有必要参数和上下文(如已初始化的 service 实例)
链式校验异常类型与消息
捕获后可继续调用 isInstanceOf()、hasMessage()、hasMessageContaining() 等方法精准验证。
立即学习“Java免费学习笔记(深入)”;
-
isInstanceOf(BusinessException.class)确保是预期的业务异常类型(而非 RuntimeException 子类泛滥) -
hasMessage("用户ID不能为零")校验完整消息内容(注意空格、标点) -
hasMessageContaining("ID", "无效")更宽松地匹配关键词,适合消息可能动态拼接的场景 - 若异常带 cause,可用
hasCauseInstanceOf(XXX.class)进一步断言底层原因
注意自定义异常的构造与 equals 行为
某些业务异常重写了 equals() 或包含不可序列化字段,可能导致断言行为异常;AssertJ 默认只比对异常实例本身,不依赖 equals。
- 无需担心自定义异常的 equals 实现,AssertJ 的
isInstanceOf和hasMessage均基于对象类型和字段值判断,不调用 equals - 若异常消息含时间戳、随机 ID 等动态内容,改用
hasMessageStartingWith()或正则matches("用户\d+不存在") - 确保异常类已正确声明(public、有合适构造函数),否则编译或运行时报错


















