2. 动机
为什么需要软断言?先看个硬断言的例子:
@Test
void test_HardAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
Assertions.assertThat(result.getId()).isNull();
Assertions.assertThat(result.getType()).isEqualTo(1);
Assertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
}
这段代码测试一个 Request
到 DomainModel
的映射器。理想情况下,所有断言都通过才算测试通过。但如果映射器有 bug(比如 id
和 status
都映射错误),运行测试会得到:
org.opentest4j.AssertionFailedError:
expected: null
but was: "73a3f292-8131-4aa9-8d55-f0dba77adfdb"
踩坑点:测试只报告了第一个错误(id
不为 null),却没提 status
也错了。因为 AssertJ 默认使用硬断言——只要有一个断言失败,测试立即终止。
在真实项目中,如果映射对象有几十个字段,反复运行测试逐个修复错误会非常耗时。软断言正是为解决这个痛点而生。
3. AssertJ 中的软断言
软断言的核心逻辑:收集所有断言错误,最后统一报告。改写上面的测试:
@Test
void test_softAssertions() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions.assertSoftly(softAssertions -> {
softAssertions.assertThat(result.getId()).isNull();
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
});
}
现在所有断言都会执行,最终生成合并错误报告:
org.assertj.core.error.AssertJMultipleFailuresError:
Multiple Failures (3 failures)
-- failure 1 --
expected: null
but was: "66f8625c-b5e4-4705-9a49-94db3b347f72"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:19)
-- failure 2 --
expected: 1
but was: 0
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:20)
-- failure 3 --
expected: "DRAFT"
but was: "NEW"
at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:21)
✅ 优势:一次运行暴露所有问题,大幅提升调试效率。
另一种写法
直接创建 SoftAssertions
实例:
@Test
void test_softAssertionsViaInstanceCreation() {
RequestMapper requestMapper = new RequestMapper();
DomainModel result = requestMapper.map(new Request().setType("COMMON"));
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(result.getId()).isNull();
softAssertions.assertThat(result.getType()).isEqualTo(1);
softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
softAssertions.assertAll(); // 关键!触发错误收集
}
⚠️ 注意:必须调用 assertAll()
才会触发错误报告。两种写法功能完全等价,按喜好选择即可。
4. 其他测试框架中的软断言
软断言是测试框架的常见功能,只是命名可能不同:
核心思想一致:失败不中断,统一报告。
5. 总结对比
硬断言 vs 软断言:
特性 | 硬断言 | 软断言 |
---|---|---|
默认行为 | ✅ | ❌ |
主流框架支持 | ✅ | ✅ |
失败时立即终止测试 | ✅ | ❌ |
6. 结论
软断言通过延迟错误报告解决了硬断言的“一次只报一个错”问题,显著提升复杂测试的调试效率。虽然 AssertJ 的实现最知名,但 JUnit 5、TestNG 等框架也提供了类似功能。
代码示例见 GitHub 仓库