如何编写ArchUnit规则检测无实际断言的AssertJ无效语句
可通过ArchUnit实现该检测需求
完全可以编写自定义ArchUnit规则,精准识别仅调用assertThat但未追加实际断言逻辑的无效AssertJ语句。这类无效语句的核心特征是:assertThat方法的返回值未被任何后续逻辑消费(既没有链式调用断言方法,也没有赋值给变量/作为参数传递),直接以分号结束语句,执行时不会产生任何断言校验效果。
规则实现代码
import com.tngtech.archunit.core.domain.JavaMethodCall; import com.tngtech.archunit.lang.ArchRule; import static com.tngtech.archunit.core.domain.JavaMethodCall.Predicates.returnsValueThatIsNotUsed; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; public class TestCodeRules { // 检测无效AssertJ断言语句的规则 public static final ArchRule ASSERTJ_ASSERTION_SHOULD_NOT_BE_USED_WITHOUT_CHECK = noClasses() // 可选:如果只需要检测测试代码,可以放开下面这行的注释,限定只扫描测试类 // .that().areAnnotatedWith("org.junit.jupiter.api.Test") .should().callMethodWhere( // 匹配AssertJ核心断言类下所有assertThat开头的方法调用 JavaMethodCall.Predicates.target(target -> target.getOwner().isAssignableTo("org.assertj.core.api.Assertions") && target.getName().startsWith("assertThat") ) // 核心判定:assertThat方法的返回值没有被后续使用 .and(returnsValueThatIsNotUsed()) ) .because("仅调用assertThat不追加断言逻辑属于无效测试代码,执行时永远不会抛出断言异常"); }
规则逻辑说明
- 匹配范围:默认覆盖所有代码中对
org.assertj.core.api.Assertions类下assertThat系列方法的调用,如果需要限定只检测测试代码,可以追加类、包路径的过滤条件 - 判定逻辑:合规写法如
assertThat(frodo.getName()).isEqualTo("Frodo")中,assertThat返回的断言对象会被用来继续调用isEqualTo等断言方法,返回值处于被消费状态,不会触发规则;违规写法如assertThat(frodo.getName())直接以分号结束,返回值被丢弃,会被规则识别为违规 - 误判规避:如果存在先将
assertThat返回值赋值给变量、后续再执行断言的写法,规则不会误报——这种场景下返回值被变量持有,不属于未被使用的情况
扩展适配
如果项目中使用了AssertJ的其他断言入口,可以按需扩展匹配范围:
- 使用软断言
SoftAssertions时,将org.assertj.core.api.SoftAssertions加入目标类匹配逻辑 - 使用BDD风格断言
BDDAssertions时,将org.assertj.core.api.BDDAssertions加入目标类匹配逻辑 - 使用自定义AssertJ扩展断言类时,将对应自定义类的全限定名加入匹配逻辑即可
内容的提问来源于stack exchange,提问作者Olaf
相关产品推荐
相关产品推荐

