Java7以降におけるMavenでのカバレッジレポート
photo: http://www.flickr.com/photos/zzpza/
Java6がEOLとなったこともあり、コンパイルバージョンもJava7以降を指定するようになったので、標準的に利用するMavenのカバレッジプラグインを見直しています。
Javaのカバレッジツールとして、今のところ有名なものとしては、以下のものがあります。
Coberturaは、Javaのカバレッジツールとしては情報量も実績も多くあります。私も、これまではCoberturaを使っていたのですが、Java7を利用するようになってからは、いろいろとエラーが発生するようになってしまいました。
JaCoCoは、情報量は少ないのですが、別のカバレッジツールであるEmmaを置き換えるためのカバレッジライブラリとして、EclEmma(Emmaを利用したEclipseプラグイン)の開発チームが、開発を進めてきたものです。
Coberturaでも、設定変更で対応できる場合もあるのですが、Cobertura自体、2年以上開発がストップしてしまっている状況でもあるので、今後はJaCoCoを利用しようかな、と考えています。
今回、それぞれの設定方法について調べたので、その内容をまとめておきます。
Cobertura レポート
基本設定をしてみる
まず初めに、Mavenにおけるコンパイルバージョンを1.7にした状態で、CoberturaのMavenプラグインであるcobertura-maven-pluginを設定して実行してみます。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>example</groupId> <artifactId>maven-coverage</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <properties> <jdk.version>1.7</jdk.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <site.encoding>UTF-8</site.encoding> </properties> <dependencies> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.2</version> <configuration> <locales>ja</locales> <inputEncoding>${project.build.sourceEncoding}</inputEncoding> <outputEncoding>${site.encoding}</outputEncoding> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> <reportSets> <reportSet> <reports> <report>report-only</report> </reports> </reportSet> </reportSets> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <configuration> <formats> <format>html</format> <format>xml</format> </formats> </configuration> </plugin> </plugins> </reporting> </project>
以下のコマンドにより、Mavenレポートを出力します。
mvn clean site
しかしながら、そのまま実行すると、以下のようなエラーが出力されてしまいます。
(Coberturaのレポートはカバレッジが0%で出力されしまうのですが、Surefireのレポートの方で、エラーが出力されています)
java.lang.VerifyError: Expecting a stackmap frame at branch target ...
Java7ビルドのエラーを回避する
先ほどのエラーを回避するためには、JUitを実行するmaven-surefire-pluginプラグインの設定を以下のように変更します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>-XX:-UseSplitVerifier</argLine> </configuration> </plugin>
今度は、カバレッジがきちんと出力されました。
それでも回避できないエラー
ただ、上記の設定をした場合でも、以下のようなエラーが発生するケースがあります。
- テストケース実行時に ArrayIndexOutOfBoundsException が発生する
Caused by: java.lang.ArrayIndexOutOfBoundsException: 54 at org.springframework.asm.Type.a(Unknown Source) at org.springframework.asm.Type.getType(Unknown Source)
これは、クラスパスに複数バージョンのasm.jarが存在するためのようです。
私の場合は、SpringFrameworkが依存関係に存在する場合に発生しました。
cobertura-maven-pluginのasm.jarを除外しようと思ったのですが、pom.xmlのreporting設定では、依存関係のjarを除外できないため、回避することができなさそうです。
また、このエラーも発生したり、しなかったりします。
- テストクラスのコンパイル時に 「XxxXxx(クラス名)にアクセスできません」というエラーが発生する
Coberturaがテストクラスのコンパイルを行うときに発生します。
これも、Coberturaが行うコンパイル時の相性によって発生するようです。
JaCoCo レポート
基本設定をしてみる
JaCoCoのMavenプラグインである、jacoco-maven-pluginの設定を行います。
build設定の部分と、reporting設定の部分とで、2箇所に設定を行う必要があります。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>example</groupId> <artifactId>maven-coverage</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <properties> <jdk.version>1.7</jdk.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <site.encoding>UTF-8</site.encoding> <jacoco.include.package>example.*</jacoco.include.package> </properties> <dependencies> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.2</version> <configuration> <locales>ja</locales> <inputEncoding>${project.build.sourceEncoding}</inputEncoding> <outputEncoding>${site.encoding}</outputEncoding> </configuration> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>${jacocoArgs}</argLine> </configuration> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <executions> <execution> <id>prepare-agent</id> <phase>test-compile</phase> <goals> <goal>prepare-agent</goal> </goals> <configuration> <propertyName>jacocoArgs</propertyName> <includes> <include>${jacoco.include.package}</include> </includes> </configuration> </execution> </executions> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> </plugin> </plugins> </reporting> </project>
JaCoCoに対して、カバレッジを出力したいパッケージのルートを指定するようにしてください。そうしないと、余計なjarを読み込もうとして、エラーが発生することがあります。
また、Coberturaの場合と違い、maven-surefire-report-pluginでは、「report-only」の設定を除外しています。JaCoCoでは、自動でテストが行われないため、レポートの出力と同時に、JUnitが実行されるようにしておく必要があります。
Mavenを実行すると、以下のようなHTMLレポートが出力されます。
日本語のディレクトリに注意
JaCoCoは、カバレッジデータを以下のファイルに出力します。
<basedir>/target/jacoco.exec
このファイルは、
自分でも、ちょっとハマりました(汗)
今後に向けて
詳細は確認できていないのですが、CoberturaとJaCoCoでは、カバレッジの値が一部異なるようです。
この辺りは、差分が生じる理由を確認する必要がありそうです。
ただ、JaCoCoは、Jenkinsプラグインもあるようなので、通常利用する分には問題無さそうです。
また、試してはいませんが、JaCoCoは、ScalaやKotlin、Xtendなどのカバレッジにも対応するようなので、有用性は高そうですね。
ということで、今後は、JaCoCoを利用していこうと思います。