Closing the Gap: Test Coverage for Quarkus Extensions

テストカバレッジは通常、テストによって実行されたコードの割合を測定する指標として定義されます。その有用性を疑う開発者もいますが、私は「テストカバレッジは、コードベースのテストされていない部分を見つけるための有用なツールである」という Martin Fowler 氏の意見に同意します。算出される数値自体が重要だとは思いません。結局のところ、生成された getter のテストを書くことには全く意味がありません。しかし、エクステンションのホットパスにおいてテストが不十分なコードを特定できれば、テストスイートを改善し、リリース前にバグを発見することができます。これは常に報われる作業です。同様に重要なこととして、リグレッションを捕捉する能力を高めることもできます。

長い間、https://www.jacoco.org/jacoco/index.html[JaCoCo コードカバレッジライブラリー]を統合する io.quarkus:quarkus-jacoco エクステンションが存在していました。しかし、最近まではエクステンションプロジェクトのランタイムモジュールのカバレッジを測定することは不可能でした。言い換えれば、@QuarkusTest ではカバレッジを測定できましたが、エクステンションのテストの大部分を占めるのが一般的な QuarkusUnitTest では測定できなかったのです。適切なカバレッジサポートがなければ、開発者は自身のバイトコード拡張やレコーディングロジックが効果的にテストされているかどうか確信が持てず、手探りの状態でした。

技術的な概要

JaCoCo は実行カバレッジデータを記録するためにクラスをインストゥルメントします。デフォルトでは、クラスは Java エージェントによってオンザフライでインストゥルメントされます。しかし、Quarkus では オフラインインストゥルメンテーションを使用して、ビルド中にクラスのバイトコードを修正します。デフォルトでは、すべてのアプリケーションアーカイブ内のすべてのクラスがインストゥルメントされます。アプリケーションアーカイブとは、アプリケーションにコンポーネントを提供するアーカイブのことです。これは Jandex を介してインデックス化され、エクステンションはその内容を分析できます。例えば、Quarkus は CDI Bean の検出中にアプリケーションアーカイブを分析します。しかし、エクステンションのランタイムモジュールは通常、アプリケーションアーカイブではありません。そのため、エクステンションのクラスはインストゥルメントされませんでした。今までは。Quarkus 3.31.2 では、インストゥルメントするアーティファクトを指定する新しい設定プロパティー quarkus.jacoco.instrument-artifacts."dependency-name".group-id と quarkus.jacoco.instrument-artifacts."dependency-name".artifact-id が導入されました。

基本設定

エクステンションのランタイムモジュールのテストカバレッジを測定したい場合は、デプロイメントモジュールで特定の JaCoCo 設定が必要になります。

<profiles>
    <profile>
        <id>test-coverage</id>
        <activation>
            <property>
                <name>jacoco</name> (1)
            </property>
        </activation>
        <dependencies>
            <dependency>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-jacoco-deployment</artifactId> (2)
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <systemPropertyVariables>
                            <quarkus.jacoco.instrument-artifacts.runtime.group-id>io.quarkus</quarkus.jacoco.instrument-artifacts.runtime.group-id>
                            <quarkus.jacoco.instrument-artifacts.runtime.artifact-id>quarkus-runtime-module-name</quarkus.jacoco.instrument-artifacts.runtime.artifact-id> (3)
                        </systemPropertyVariables>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
1 このプロファイルは jacoco プロパティーで有効になります。
2 テストスコープで quarkus-jacoco-deployment 依存関係を追加します。
3 io.quarkus:quarkus-runtime-module-name アーティファクトをインストゥルメントするように JaCoCo プラグインに指示します。

デフォルトでは、JaCoCo データは target/jacoco-quarkus.exec ファイルに保存され、カバレッジレポートは target/jacoco-report ディレクトリに自動的に生成されます。

マルチモジュール設定

マルチモジュールプロジェクトの場合、さらに多くの設定プロパティーが必要になる場合があります。通常、エクステンションプロジェクトに互いに依存する複数のエクステンションが含まれている場合、より複雑な設定が必要になります。以下のプロジェクトでは、foo と bar という 2 つのエクステンションサブモジュールがあります。

/my-extension-project
   ├── /foo
   │  ├── runtime
   │  ├── deployment
   │  └── pom.xml
   ├── /bar (depends on foo)
   │  ├── runtime
   │  ├── deployment
   │  └── pom.xml
   └── pom.xml

サブモジュール bar は foo に依存し、その機能を拡張します。foo/deployment サブモジュールには、foo/runtime のクラスをテストする QuarkusUnitTest が含まれています。bar/deployment サブモジュールには、bar/runtime と foo/runtime の両方のクラスをテストする QuarkusUnitTest が含まれています。

私たちの目標は、foo/runtime と bar/runtime の両方のカバレッジを測定することです。どのように進めればよいでしょうか。まず、親プロジェクトにいくつかの設定を適用する必要があります。

my-extension-project/pom.xml
<profiles>
    <profile>
        <id>test-coverage</id>
        <activation>
            <property>
                <name>jacoco</name> (1)
            </property>
        </activation>
        <build>
            <plugins>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <systemPropertyVariables>
                           <quarkus.jacoco.data-file>${maven.multiModuleProjectDirectory}/target/jacoco.exec</quarkus.jacoco.data-file> (2)
                           <quarkus.jacoco.reuse-data-file>true</quarkus.jacoco.reuse-data-file> (3)
                           <quarkus.jacoco.report-location>${maven.multiModuleProjectDirectory}/target/coverage</quarkus.jacoco.report-location> (4)
                           <quarkus.jacoco.aggregate-report-data>true</quarkus.jacoco.aggregate-report-data> (5)
                        </systemPropertyVariables>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
1 このプロファイルは jacoco プロパティーで有効になります。
2 共有される JaCoCo データファイルは my-extension-project/target/jacoco.exec になります。
3 共有された JaCoCo データはすべてのテストで再利用されます。
4 生成されるカバレッジレポートは my-extension-project/target/coverage ディレクトリに出力されます。
5 レポートデータ (ソースディレクトリとクラスファイル) は、単一のレポートを生成できるように集計されます。

その後、foo と bar のデプロイメントモジュールを修正します。

my-extension-project/bar/pom.xml
<profiles>
   <profile>
      <id>test-coverage</id>
      <activation>
         <property>
            <name>jacoco</name> (1)
         </property>
      </activation>
      <dependencies>
         <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jacoco-deployment</artifactId>
            <scope>test</scope>
         </dependency>
      </dependencies>
      <build>
         <plugins>
            <plugin>
               <artifactId>maven-surefire-plugin</artifactId>
               <configuration>
                  <systemPropertyVariables>
                     <quarkus.jacoco.instrument-artifacts.foo.group-id>org.acme</quarkus.jacoco.instrument-artifacts.foo.group-id>
                     <quarkus.jacoco.instrument-artifacts.foo.artifact-id>foo</quarkus.jacoco.instrument-artifacts.foo.artifact-id> (2)
                     <quarkus.jacoco.instrument-artifacts.bar.group-id>org.acme</quarkus.jacoco.instrument-artifacts.bar.group-id>
                     <quarkus.jacoco.instrument-artifacts.bar.artifact-id>bar</quarkus.jacoco.instrument-artifacts.bar.artifact-id> (3)
                  </systemPropertyVariables>
               </configuration>
            </plugin>
         </plugins>
      </build>
   </profile>
</profiles>
1 このプロファイルは jacoco プロパティーで有効になります。
2 bar/deployment でテストを実行する際に org.acme:foo をインストゥルメントします。
3 bar/deployment でテストを実行する際に org.acme:bar をインストゥルメントします。

同様に my-extension-project/foo/pom.xml を修正し、mvn clean test -Djacoco を実行するだけです。ビルドが完了すると、my-extension-project/target/coverage ディレクトリにあるコードカバレッジレポートを分析できます。

The Quarkus JaCoCo config only works for tests that are annotated with @QuarkusTest and @QuarkusUnitTest. If you want to check the coverage of other tests as well then you will need to fall back to the JaCoCo maven plugin, see the docs for more information.

まとめ

Quarkus 3.31.2 以降、Quarkus エクステンションのランタイムモジュールのコードカバレッジを測定し、分析できるようになりました。テストスイートを改善し、リリース前に多くのバグを発見し、より多くのリグレッションを捕捉しましょう。