変えたのは Lombok の版だけ。ソースもテストも JaCoCo の版もそのままで、カバレッジレポートの数字が入れ替わります。
| 集計 | Lombok 1.18.32 | Lombok 1.18.46 |
|---|---|---|
| METHOD(母数 / 被覆) | 8 / 5 = 62.50% | 2 / 2 = 100.00% |
| LINE(母数 / 被覆) | 5 / 4 = 80.00% | 2 / 2 = 100.00% |
| INSTRUCTION(母数 / 被覆) | 30 / 20 = 66.67% | 9 / 9 = 100.00% |
BUILD SUCCESS は両方とも変わりません。instrumentation の例外も出ない。率は下がるのではなく上がっています。
そのため、カバレッジの閾値チェックは止める側に回りませんでした。同じ設定(jacoco:check・METHOD 80%)での実測です。
lombok 1.18.32:
[WARNING] Rule violated for bundle cov: methods covered ratio is 0.62, but expected minimum is 0.80
[INFO] BUILD FAILURE
lombok 1.18.46:
[INFO] All coverage checks have been met.
[INFO] BUILD SUCCESS
テストは 1 行も足していません。落ちていたゲートが、依存を 1 つ上げたら通ります。
手がかりになるのは、レポートの母数だけです。エラーメッセージも警告も出ないので、jacoco.xml の
<counter type="METHOD" missed=".." covered=".."/> か、HTML レポートの Missed Methods の列を、
更新の前後で見比べることになります。
何が変わったのか
Lombok は 1.18.34 から、生成したメンバへ lombok.@Generated を既定で付けます。javap で数えたものです。
javap -v -p dev.errfix.Profile | grep -c lombok/Generated
lombok 1.18.32 → 0
lombok 1.18.34 → 1
lombok 1.18.46 → 1
JaCoCo は、単純名に Generated を含む注釈(保持方針が class か runtime)が付いたクラス・メソッドを
レポートの集計から外します。この除外自体は JaCoCo 0.8.2(2018 年)からある動作で、新しくありません。
新しいのは、Lombok がその注釈を既定で付けるようになったことです。
reproduce したプロジェクトに lombok.config は置いていません。設定を誰かが変えたのではなく、既定動作が
動いたことによる変化になります。
境界は Lombok 側の 1.18.32 と 1.18.34 のあいだ。JaCoCo の版を 0.8.11 / 0.8.13 / 0.8.15 と振っても、 同じ Lombok に対する集計は 3 つとも同じ値でした(除外の実装が 0.8.2 から入っているため)。
母数が縮む範囲
消えるのは Lombok が生成したメンバだけで、手で書いたコードは残ります。上の表の 8 → 2 の内訳です。
| メンバ | 由来 | 1.18.32 | 1.18.46 |
|---|---|---|---|
| 既定コンストラクタ | 手書きクラスの暗黙生成 | 数える | 数える |
describe() | 手書き | 数える | 数える |
getName / setName ほか 6 つ | @Getter / @Setter | 数える | 数えない |
@Data を付けたクラスは生成されるメンバが多いので、減り幅も大きくなります。@Data +
@NoArgsConstructor + @AllArgsConstructor の DTO 1 つと、手書きのクラス 1 つを置いて測った例です。
| 集計 | Lombok 1.18.32 | Lombok 1.18.46 |
|---|---|---|
| CLASS(母数) | 2 | 1 |
| METHOD(母数 / 被覆) | 12 / 4 = 33.33% | 2 / 2 = 100.00% |
| LINE(母数 / 被覆) | 7 / 4 = 57.14% | 2 / 2 = 100.00% |
| BRANCH(母数) | 16 | 集計に現れない |
全メンバが生成物であるクラスは、クラス単位でも数えられなくなります(CLASS の母数が 2 から 1 へ)。
jacoco.xml にはその <class> 要素自体が残り、減るのはカウンタへの寄与だけです。@Data が作る equals / hashCode の分岐が丸ごと外れるため、BRANCH のカウンタは
集計そのものから姿を消します。
どちらの挙動を選ぶか
生成コードをカバレッジの対象外にするのは、それ自体としては筋の通った既定です。getName() を呼ぶだけの
テストを書いて率を保つ作業には意味がない、という立場になる。この記事が扱うのは「どちらが正しいか」ではなく、
版を上げた時点で挙動が切り替わり、レポートの数字に断絶ができることのほうです。
数字を旧版と揃えたい場合は、lombok.config に次の 1 行を置きます。
lombok.addLombokGeneratedAnnotation = false
Lombok は各ソースファイルのディレクトリから上へたどって lombok.config を探すので、そのソースの祖先
ディレクトリに無いと読まれません。src/main/java を持つ単一モジュールなら、その src と同じ階層
(プロジェクト直下)で読まれます。
マルチモジュールでは、置いた場所だけでは決まりません。 設定はディレクトリとその配下に効き、上の階層へ
たどって集まりますが、ソースファイルに近い設定のほうが優先します。子モジュール側に別の lombok.config が
あれば、集約プロジェクト直下に置いた値はそのモジュールに届きません。config.stopBubbling = true が途中にあれば、
そこから上は読まれなくなります。この記事が実測したのは単一モジュールだけなので、複数モジュールの構成では
実効値を確かめてください。Lombok 自身がその手段を持っています。
java -jar lombok.jar config <ソースファイル or ディレクトリ>
生成物の側から確かめる手もあります。
javap -v -p -cp target/classes dev.errfix.Profile | grep -c lombok/Generated
→ 0 なら、注釈は付いていない
設定を戻した実測では、METHOD の母数が 8 に、率が 62.50% に戻りました。
新しい既定のまま進めるなら、閾値の再設定を同じコミットで行います。 母数が変われば率の意味も変わります。
更新前の閾値を据え置くと、上の check のように基準が緩んだ状態でビルドが成功し続けます。
切り分け
- カバレッジが 0% になった / レポートが空:これは母数が縮む話ではなく、計測そのものが動いていない状態です。
prepare-agentの実行が設定されているか、Surefire のargLineを上書きして JaCoCo のエージェント指定を 消していないかを見ます。本記事の境界とは別です。 - 率が下がった:除外が率をどちらへ動かすかは、外れたメンバの被覆率が全体の率より低いか高いかで決まります。 生成アクセサは呼ばれずに残ることが多いので、実測ではどれも上がる向きでした。下がったなら、外れた生成メンバの ほうがよく被覆されていた構成か、同じ更新に別の変更が混ざっています。
- テストの実行件数そのものが減っている:カバレッジではなくテストの収集の問題です。 Tests run: 0 のままビルドが成功する が同じ 「成功しているのに減る」型を扱っています。あちらは実行件数、こちらは集計の母数。
- 同じ Lombok 更新でコンパイルが止まった:症状が別で、原因も別です。JDK 23 以降で注釈処理が既定で 走らなくなった件は Lombok の getter が cannot find symbol になる にまとめてあります。
- Gradle で同じことが起きているか:
lombok.@Generatedの付与は Lombok 側なのでビルドツールに依らず、 JaCoCo の除外も同じ実装です。ただし本記事で実測したのは Maven の構成だけです。
検証環境
- image:
eclipse-temurin(digest 固定sha256:68868d04...)。コンパイルと実行は同じイメージ内の OpenJDK 17(JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64)に固定。Maven 3.9.12、Surefire 3.5.2 - 再現:同一ソース・同一テストで Lombok 1.18.32 と 1.18.34 の 2 つのプロジェクトをそれぞれ
mvn clean testし、jacoco.xmlの report レベルのMETHOD母数を比較。1.18.34 側が小さいことを判定スクリプトが確認して 終了コード 1 とシグネチャを出す(Maven は両方とも成功している。1 を返すのは判定スクリプト) - 修正:Lombok は 1.18.34 のまま
lombok.configにlombok.addLombokGeneratedAnnotation = falseを置き、 母数が 1.18.32 と一致することを確認 → 終了コード 0 - 冒頭の表と
jacoco:checkの実測は、一部のアクセサだけをテストが触る別のプロジェクトで採取した (ケースのクラスは全メンバが被覆済みで 100% になり、率の動きが出ないため)。3 フィールドの@Getter/@Setterと手書きのdescribe()を持ち、テストはdescribe()と一部のアクセサだけを呼ぶ - JaCoCo は 0.8.11 / 0.8.13 / 0.8.15 で比較し、同じ Lombok に対して同値であることを確認
lombok.@Generatedの有無はjavap -v -pの出現数で確認(1.18.32 / 1.18.34 / 1.18.46 と、 設定を戻した 1.18.46)@Dataの表は、@Data+@NoArgsConstructor+@AllArgsConstructorの DTO と手書きクラス 1 つを 置いた 3 つ目のプロジェクトで採取(JaCoCo 0.8.15)。<class>要素が残ることもjacoco.xmlで確認しているlombok.configの探索規則(配下に効く/上へたどる/近い設定が優先する/config.stopBubbling)は Lombok の Configuration system のドキュメントによる。実測は単一モジュールのみで、マルチモジュールの 優先順位は本記事では確かめていない- 本文がコピペ用に指しているのは Maven Central の現行(Lombok 1.18.46、jacoco-maven-plugin 0.8.15)。 ケースが検証した 1.18.32 / 1.18.34 / 0.8.13 は境界を示す点として残している
再現から修正までは errfix の検証ハーネスが機械的に確認しています。JaCoCo が単純名に Generated を含む注釈を
集計から外すことと、その動作が 0.8.2 で入ったこと(0.8.3 で完全一致から部分一致へ広がったこと)は、
JaCoCo の変更履歴で確認しました。