CLOVER🍀

That was when it all began.

OpenCloverでカバレッジを取得する(Maven Simple Project編)

2017年の春に、Atlassian Cloverというカバレッジを取得するツールがOSSになりました。

Atlassian Clover がオープンソースに | Atlassian Blogs

OpenClover - Java, Groovy and AspectJ code coverage tool

ということで、ちょっと試してみようかと。

お題

とりあえず、簡単にカバレッジを取得するサンプルを書いてみます。

こんなお題で。

  • Mavenプロジェクト(単一モジュール)で構成
  • メインのコードもテストコードもそれぞれひとつずつ
  • テストを実行したら、カバレッジレポートを出力したい

追記
マルチプロジェクト版は、こちら。

OpenCloverでカバレッジを取得する(Maven Multi Project編) - CLOVER

参考ドキュメント

参考ドキュメントとしては、このあたりを。

OpenClover 4.2 : Clover-for-Maven Quick Start Guide

OpenClover 4.2 : Clover-for-Maven tutorials

OpenClover 4.2 : Clover-for-Maven User's Guide

OpenClover 4.2 : Basic usage

Clover Maven Plugin – Maven 2 Clover Plugin Introduction

チュートリアルは、Bitbucketへのリンクとなっています。

clover-maven-pluginのsrc/itにサンプルがあるということなので

openclover / clover-maven-plugin / source / src / it — Bitbucket

単純なプロジェクト構成と

openclover / clover-maven-plugin / source / src / it / simple — Bitbucket

マルチプロジェクト構成とかを見ておくとよいかもしれません。

openclover / clover-maven-plugin / source / src / it / multiproject — Bitbucket

とりあえずサンプルコード

まずは、サンプルコードを書いてみます。

最初は、OpenCloverの設定抜きで。

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.0.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.8.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19</version>
                <dependencies>
                    <dependency>
                        <groupId>org.junit.platform</groupId>
                        <artifactId>junit-platform-surefire-provider</artifactId>
                        <version>1.0.1</version>
                    </dependency>
                    <dependency>
                        <groupId>org.junit.jupiter</groupId>
                        <artifactId>junit-jupiter-engine</artifactId>
                        <version>5.0.1</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

テスト対象コード。
src/main/java/org/littlewings/example/CalcService.java

package org.littlewings.example;

public class CalcService {
    public int add(int a, int b) {
        return a + b;
    }

    public int minus(int a, int b) {
        return a - b;
    }

    public int multiply(int a, int b) {
        return a * b;
    }
}

テストコード。
src/test/java/org/littlewings/example/CalcServiceTest.java

package org.littlewings.example;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class CalcServiceTest {
    @Test
    public void add() {
        CalcService calcService = new CalcService();
        assertThat(calcService.add(1, 2)).isEqualTo(3);
    }

    @Test
    public void multiply() {
        CalcService calcService = new CalcService();
        assertThat(calcService.multiply(2, 3)).isEqualTo(6);
    }
}

minusへのテストがありません。

OpenCloverの設定をする

では、ここからOpenCloverの設定をpom.xmlに加えてみます。

参考にするドキュメントは、こちらがよい気がします。

OpenClover 4.2 : Basic usage

プラグインに、OpenCloverのMaven Pluginを加えます。

            <plugin>
                <groupId>org.openclover</groupId>
                <artifactId>clover-maven-plugin</artifactId>
                <version>4.2.0</version>
            </plugin>

siteゴール用に、reporting設定も加えておいてもよいでしょう。

    <reporting>
        <plugins>
            <plugin>
                <groupId>org.openclover</groupId>
                <artifactId>clover-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </reporting>

この状態で、ドキュメントに倣って

$ mvn clean clover:setup test clover:aggregate clover:clover

と実行すると「target/site/clover」ディレクトリに、レポートができています。

$ ls -l target/site/clover/index.html 
-rw-rw-r-- 1 xxxxx xxxxx 412 114 21:26 target/site/clover/index.html

「Code coverage」が66.7%なのは、テストしていないメソッドがあるからですね。

もしくは、「clover:setup」の部分を「clover:instrument」にしてもOKです。

$ mvn clean clover:instrument test clover:aggregate clover:clover

作成されるレポートは、省略します。

「clover:setup」と「clover:instrument」の違いは、次のとおりです。

The clover:setup goal performs instrumentation in the main build life cycle, therefore it's not recommended to use it together with 'install' or 'deploy' goals (unless this is your intention). The benefit of this approach is that build is made only once. Furthermore, clover:setup supports instrumentation of Groovy code (while clover:instrument does not).

The clover:instrument goal can be used if you need to actually install or deploy non-instrumented project's artifact and have Clover run at the same time. This will fork a custom build life cycle and run it till the "verify" phase and cause each Clover artifact to contain the -clover classifier. A drawback of this approach is that the build and tests are performed twice.

http://openclover.org/doc/manual/4.2.0/maven--basic-usage.html#Basicusage-Usingclover:setupvsclover:instrumentvsclover:instrument-test

「clover:setup」だと、メインのビルドライフサイクルに組み込まれてしまうので、「install」や「deploy」のゴールと一緒に使うのは
お勧めされません。

これは、「clover:setup」の後の「target/classes」ディレクトリを見るとわかります。

$ ls -l target/classes/org/littlewings/example
合計 12
-rw-rw-r-- 1 xxxxx xxxxx 1713 114 21:33 CalcService$__CLR4_2_000j9lb8369$1.class
-rw-rw-r-- 1 xxxxx xxxxx 3840 114 21:33 CalcService$__CLR4_2_000j9lb8369.class
-rw-rw-r-- 1 xxxxx xxxxx 1339 114 21:33 CalcService.class

OpenCloverによって拡張されたクラスファイルが生成されています。

「clover:instrument」の場合だと、「target/classes」配下は通常通りで、「target/clover/classes」配下に先ほどの「clover:setup」で
生成されていたようなファイルが出力されています。

$ ls -l target/classes/org/littlewings/example
合計 4
-rw-rw-r-- 1 xxxxx xxxxx 591 114 21:35 CalcService.class


$ ls -l target/clover/classes/org/littlewings/example
合計 12
-rw-rw-r-- 1 xxxxx xxxxx 1713 114 21:35 CalcService$__CLR4_2_000j9lbafwn$1.class
-rw-rw-r-- 1 xxxxx xxxxx 3840 114 21:35 CalcService$__CLR4_2_000j9lbafwn.class
-rw-rw-r-- 1 xxxxx xxxxx 1339 114 21:35 CalcService.class

「clover:instrument」を使用すると、プロダクションコードとOpenClover込みのコードを分離できますが、代わりとして
テストが2回実行されます

通常、「verify」などと合わせて使い、「-clover」サフィックスアーティファクト(classfier)が生成されるので、
こちらを使用するようです。

補足)
「-clover」サフィックスアーティファクトが生成される場合は、「target/clover」ディレクトリ配下に生成されることになります。

$ ls -l target/clover/openclover-simple-0.0.1-SNAPSHOT-clover.jar 
-rw-rw-r-- 1 xxxxx xxxxx 6872 114 21:48 target/clover/openclover-simple-0.0.1-SNAPSHOT-clover.jar

OpenCloverのMaven Pluginのサンプルなどが、instrumentを使いつつverifyを想定したサンプルを書いているのは、
このあたりが理由かなと思います。

openclover / clover-maven-plugin / source / src / it / simple / pom.xml — Bitbucket

ちなみに、他の「clover:aggregate」と「clover:clover」の意味ですが、aggregateはMavenマルチモジュールの場合に各モジュールの
カバレッジを集約するために使い、cloverでレポートが生成されます。

つまり、今回のサンプルではaggregateは意味がありません…。

外しても、問題なくレポートが生成されます。

$ mvn clean clover:setup test clover:clover

「mvn test」でカバレッジレポートを生成したい場合

「mvn test」くらいで、ここまでの処理を実行したい場合は、次のように書くとよいでしょう。

            <plugin>
                <groupId>org.openclover</groupId>
                <artifactId>clover-maven-plugin</artifactId>
                <version>4.2.0</version>
                <executions>
                    <execution>
                        <id>clover-instrument</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <!-- <goal>setup</goal>  -->
                            <goal>instrument</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>clover-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>aggregate</goal>
                            <goal>clover</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

「setup」か「instrument」は、用途に応じて選ぶ感じで。

「mvn verify」時に動作するように設定する

こちらのサンプルを参考にするとよいでしょう。

openclover / clover-maven-plugin / source / src / it / simple / pom.xml — Bitbucket

ただ、このサンプルのままだと「clover:clover」がないのでレポートが生成されません。つける場合は、こんな感じで。

            <plugin>
                <groupId>org.openclover</groupId>
                <artifactId>clover-maven-plugin</artifactId>
                <version>4.2.0</version>
                <executions>
                    <execution>
                        <id>main</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>instrument</goal>
                            <goal>check</goal>
                            <goal>clover</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

あとは、「mvn verify」で。

$ mvn clean verify

ここでは「clover:instrument」を使用したので、テストが2回動きますが…。