The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

テストカバレッジの測定

アプリケーションのテストカバレッジを測定する方法をご紹介します。このガイドでは、以下の内容をカバーしています:

  • ユニットテストのカバレッジを測定する

  • 統合テストのカバレッジを測定する

  • ユニットテストと統合テストの実行を分離する

  • 全テストのカバレッジを統合する

ネイティブモードではコードカバレッジはサポートされていませんのでご注意ください。

1. 前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 11+ がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.8.1+

  • 使用したい場合、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

  • アプリケーションのテストガイド が完了済であること

2. アーキテクチャ

このガイドで作成するアプリケーションは、サービスを使用するために依存性の注入を使用した JAX-RS エンドポイント (hello world) です。サービスは JUnit 5 でテストされ、エンドポイントには @QuarkusTest のアノテーションが付けられます。

3. ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。しかし、完成した例にすぐに進むことができます。Git リポジトリをクローンします: git clone https://github.com/quarkusio/quarkus-quickstarts.git 、または archive をダウンロードします。

ソリューションは tests-with-coverage-quickstart ディレクトリ にあります。

4. 簡単なプロジェクトと2つのテストから始める

Quarkus Mavenプラグインで作成した空のアプリケーションから始めてみましょう:

CLI
quarkus create app org.acme:tests-with-coverage-quickstart \
    --extension=resteasy-reactive \
    --no-code
cd tests-with-coverage-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストール方法については、Quarkus CLIガイドをご参照ください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=tests-with-coverage-quickstart \
    -Dextensions="resteasy-reactive" \
    -DnoCode
cd tests-with-coverage-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

ここで、アプリケーションをテストで適切にカバーするために必要な要素をすべて追加していきます。

最初に、helloエンドポイントを提供するアプリケーション:

package org.acme.testcoverage;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

    private final GreetingService service;

    @Inject
    public GreetingResource(GreetingService service) {
        this.service = service;
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(String name) {
        return service.greeting(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

このエンドポイントは、グリーティングサービスを利用しています:

package org.acme.testcoverage;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {

    public String greeting(String name) {
        return "hello " + name;
    }

}

また、プロジェクトにはテストも必要です:

package org.acme.testcoverage;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Tag;

import java.util.UUID;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
          .when().get("/hello")
          .then()
             .statusCode(200)
             .body(is("hello"));
    }

    @Test
    public void testGreetingEndpoint() {
        String uuid = UUID.randomUUID().toString();
        given()
          .pathParam("name", uuid)
          .when().get("/hello/greeting/{name}")
          .then()
            .statusCode(200)
            .body(is("hello " + uuid));
    }
}

5. Jacocoの設定

次に、Jacocoをプロジェクトに追加する必要があります。そのために、ビルドファイルに以下を追加する必要があります:

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-jacoco</artifactId>
  <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.quarkus:quarkus-jacoco")

このQuarkusエクステンションは、通常はJacoco Mavenプラグインで行われることすべての処理を行うため、追加の設定は必要ありません。

エクステンションとプラグインの両方を使用するには、特別な設定が必要です。両方を追加すると、すでに組み込まれているクラスに関する多くのエラーが発生します。必要な設定の詳細は以下のとおりです。

6. カバレッジ付きテストの実行

mvn verify を実行すると、テストが実行され、その結果が target/jacoco-reports に出力されます。 quarkus-jacoco のエクステンションを使えば、これだけでJacoco はすぐに動作します。

これにはいくつかの設定オプションがあります:

ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能

Configuration property

タイプ

デフォルト

The jacoco data file

Environment variable: QUARKUS_JACOCO_DATA_FILE

string

jacoco-quarkus.exec

Whether to reuse (true) or delete (false) the jacoco data file on each run.

Environment variable: QUARKUS_JACOCO_REUSE_DATA_FILE

boolean

false

If Quarkus should generate the Jacoco report

Environment variable: QUARKUS_JACOCO_REPORT

boolean

true

Encoding of the generated reports.

Environment variable: QUARKUS_JACOCO_OUTPUT_ENCODING

string

UTF-8

Name of the root node HTML report pages.

Environment variable: QUARKUS_JACOCO_TITLE

string

Footer text used in HTML report pages.

Environment variable: QUARKUS_JACOCO_FOOTER

string

Encoding of the source files.

Environment variable: QUARKUS_JACOCO_SOURCE_ENCODING

string

UTF-8

A list of class files to include in the report. May use wildcard characters (* and ?). When not specified everything will be included. For instance: - **/fo/**/* targets all classes under fo and sub packages - **/bar/* targets all classes directly under bar - **/*BAR*.class targets classes that contain BAR in their name regardless of path

Environment variable: QUARKUS_JACOCO_INCLUDES

list of string

**

A list of class files to exclude from the report. May use wildcard characters (* and ?). When not specified nothing will be excluded. For instance: - **/fo/**/* targets all classes under fo and sub packages - **/bar/* targets all classes directly under bar - **/*BAR*.class targets classes that contain BAR in their name regardless of path

Environment variable: QUARKUS_JACOCO_EXCLUDES

list of string

The location of the report files.

Environment variable: QUARKUS_JACOCO_REPORT_LOCATION

string

jacoco-report

7. QuarkusTestを使用していないテストのカバレッジ

Quarkusの自動Jacoco設定は、 @QuarkusTest でアノテーションされたテストに対してのみ機能します。他のテストのカバレッジもチェックしたい場合は、Jacoco mavenプラグインを利用する必要があります。

quarkus-jacoco エクステンションを pom.xml に含めるのに加えて、以下のような設定が必要になります:

pom.xml
<project>
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                   <execution>
                      <id>default-prepare-agent</id>
                      <goals>
                           <goal>prepare-agent</goal>
                      </goals>
                      <configuration>
                        <exclClassLoaders>*QuarkusClassLoader</exclClassLoaders>  (1)
                        <destFile>${project.build.directory}/jacoco-quarkus.exec</destFile>
                        <append>true</append>
                      </configuration>
                   </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
1 この設定は、 QuarkusClassLoader によってロードされる @QuarkusTest 関連のクラスを無視するようになっています
build.gradle
plugins {
    id 'jacoco' (1)
}

test {
    finalizedBy jacocoTestReport
    jacoco {
        excludeClassLoaders = ["*QuarkusClassLoader"] (2)
        destinationFile = layout.buildDirectory.file("jacoco-quarkus.exec").get().asFile (2)
    }
    jacocoTestReport.enabled = false (3)
}
1 jacoco gradleプラグインの追加
2 この設定は、 QuarkusClassLoader によってロードされる @QuarkusTest 関連のクラスを無視するようになっています
3 quarkus-jacoco エクステンションも使用していて、少なくとも 1 つの @QuarkusTest がある場合は、この設定を false にします。デフォルトの jacocoTestReport タスクは省略できます。なぜなら、通常のユニットテストと @QuarkusTest クラスの実行データが同じファイルに記録されているため、 quarkus-jacoco がそれらを合わせたレポートを生成するからです。
この設定は、少なくとも1つの @QuarkusTest が実行されている場合にのみ機能します。 @QuarkusTest を使用していない場合は、追加の設定をすることなく、通常の方法で Jacoco プラグインを使用することができます。

7.1. 結合テストのカバレッジ

結合テストからコードカバレッジデータを取得するには、以下の要件を満たす必要があります:

  • ビルドされたアーティファクトがjarであること(コンテナやネイティブバイナリではないこと)。

  • Jacocoがビルドツールで設定されていること。

  • アプリケーションは、 quarkus.package.write-transformed-bytecode-to-build-outputtrue に設定してビルドされていること

quarkus.package.write-transformed-bytecode-to-build-output=true は慎重に設定を行う必要があります。 後続のビルドがクリーンな環境で行われる場合、つまりビルドツールの出力ディレクトリが完全にクリーンである場合のみ行う必要があります。

pom.xml で、Jacocoのプラグイン設定を以下のように追加します。これにより、結合テストのデータをユニットテストと同じ保存先のファイルに追加し、結合テストが完了した後にjacocoレポートを再構築することで、包括的なコードカバレッジレポートを作成します。

<build>
    ...
    <plugins>
        ...
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>${jacoco.version}</version>
            <executions>
                ... (1)

                <execution>
                    <id>default-prepare-agent-integration</id>
                    <goals>
                        <goal>prepare-agent-integration</goal>
                    </goals>
                    <configuration>
                        <destFile>${project.build.directory}/jacoco-quarkus.exec</destFile>
                        <append>true</append>
                    </configuration>
                </execution>
                <execution>
                    <id>report</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                    <configuration>
                        <dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
                        <outputDirectory>${project.build.directory}/jacoco-report</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
    ...
</build>
1 すべてのexecutionsは同じ <plugin> の定義にあるはずなので、すべてつなげてください。

Jacocoエージェントで結合テストをjarとして実行するには、 pom.xml に以下を追加します。

<build>
    ...
    <plugins>
        ...
        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>${surefire-plugin.version}</version>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                    <configuration>
                        <systemPropertyVariables>
                            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                            <maven.home>${maven.home}</maven.home>
                            <quarkus.test.arg-line>${argLine}</quarkus.test.arg-line>
                        </systemPropertyVariables>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
    ...
</build>
quarkus.test.arg-line で同じ値を共有すると、異なるタイプの Quarkus アーティファクトをテストする統合テストの実行が中断される場合があります。このような場合は、Mavenプロファイルの使用することをお勧めします。

8. カバレッジの閾値を設定

JaCoCo Maven プラグインを使用して、コードカバレッジの閾値を設定することができます。注意点としては、 <dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile> があり、 quarkus.jacoco.data-file の設定と一致するように設定が必要です。

pom.xml
<build>
    ...
    <plugins>
        ...
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>${jacoco.version}</version>
            <executions>
                ... (1)

                <execution>
                    <id>jacoco-check</id>
                    <goals>
                        <goal>check</goal>
                    </goals>
                    <phase>post-integration-test</phase>
                    <configuration>
                        <dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
                        <rules>
                            <rule>
                                <element>BUNDLE</element>
                                <limits>
                                    <limit>
                                        <counter>LINE</counter>
                                        <value>COVEREDRATIO</value>
                                        <minimum>0.8</minimum>
                                    </limit>
                                    <limit>
                                        <counter>BRANCH</counter>
                                        <value>COVEREDRATIO</value>
                                        <minimum>0.72</minimum>
                                    </limit>
                                </limits>
                            </rule>
                        </rules>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        ...
    </plugins>
    ...
</build>
1 すべてのexecutionsは同じ <plugin> の定義にあるはずなので、すべてつなげてください。
build.gradle
jacocoTestCoverageVerification {
    executionData.setFrom("$project.buildDir/jacoco-quarkus.exec")
    violationRules {
        rule {
            limit {
                counter = 'INSTRUCTION'
                value = 'COVEREDRATIO'
                minimum = 0.80
            }
            limit {
                counter = 'BRANCH'
                value = 'COVEREDRATIO'
                minimum = 0.72
            }
        }
    }
}
check.dependsOn jacocoTestCoverageVerification

検証タスクからクラスを除外するには、以下のように設定します:

jacocoTestCoverageVerification {
    afterEvaluate { (1)
        classDirectories.setFrom(files(classDirectories.files.collect { (2)
            fileTree(dir: it, exclude: [
                    "org/example/package/**/*" (3)
            ])
        }))
    }
}
1 Gradle では、評価フェーズの後に classDirectories を読み込む必要があります
2 現在、GradleのJaCoCoには、 excludes をこのように指定する必要があります。 - https://github.com/gradle/gradle/issues/14760 の問題が修正されると、除外されます
3 org/example/package パッケージに含まれるすべてのクラスを除外します

9. まとめ

これで、テストのカバレッジを調べるのに必要な情報はすべて揃いました! しかし、カバーされていないコードは、十分にテストされていないものがあることを忘れないでください。 カバーされているコードが必ずしも 十分に テストされていないものもあります。良いテストを書くようにしましょう!