结论先说:
• 规则 “Tests should include assertions” (S2975) 依旧会执行,只提供 target/classes
并不会让它完全失效;
• 但准确率会下降——某些写法(特别是 JUnit5 自定义断言、第三方断言库、继承自公共父类的断言方法)可能被漏报或误报。
因此最佳做法仍然是把 target/test-classes
也加入 sonar.java.binaries
。
────────────────────────────────────────
为什么缺少 test-classes 仍能运行?
────────────────────────────────────────
-
S2975 属于 SonarJava 静态语法规则:
它主要在源码 AST 里查找
• 是否是测试文件(根据包名、类名、JUnit 注解、TestNG 注解等)
• 每个测试方法里是否出现了assert*
、fail()
、assertThat
等调用
——这一步不依赖字节码就能完成,所以规则照常执行。 -
解析失败时的 fallback:
如果某个断言方法解析不到符号,SonarJava 会继续按“文本匹配 + 简化推断”方式尝试,不会直接报 fatal error。
────────────────────────────────────────
为什么建议一定提供 test-classes?
────────────────────────────────────────
-
完整类型解析
‒ 对 JUnit 5 的@TestFactory
、@ParameterizedTest
、元注解
‒ 对继承父类中的自定义assertFoo()
若没有 test 字节码,Sonar 无法解析这些符号,可能漏报。 -
降低误报
有时测试代码里会调用业务代码方法名中带 “assert”,解析不到类型信息时可能被误判为断言;字节码能让分析器区分两者。 -
其他规则也需要
像 S2187(测试类必须包含测试方法)、S2699(应至少有一个断言)同理受影响;
还有依赖字节码的复杂规则(循环复杂度、无用代码)对测试代码也更准确。
────────────────────────────────────────
如何一次性写好
────────────────────────────────────────
# 收集所有主包字节码
mainBin=$(find . -type d -path '*/target/classes' -print | paste -sd',')
# 收集所有测试字节码
testBin=$(find . -type d -path '*/target/test-classes' -print | paste -sd',')
${SCAN}/bin/sonar-scanner \
-Dsonar.java.binaries=${mainBin},${testBin} \
...
多个目录用逗号分隔;Maven/Gradle 多模块同理。
────────────────────────────────────────
如果实在拿不到 test-classes?
────────────────────────────────────────
• 规则仍会运行,只要确认
-Dsonar.tests=<test-source-dirs>
能把测试源码目录标记正确;
• 接受可能的漏报 / 误报,或在 Quality Profile 里放宽 “Missing assertion” 的严重级别;
• 尝试在 CI 里加入 mvn test-compile
或 gradle testClasses
,让测试字节码至少被编译生成一次,再做 Sonar 扫描。
这样就能兼顾扫描速度与规则准确性,同时避免因为缺少 test-classes
带来的潜在误差。