これは、なにをしたくて書いたもの?
JaCoCo Maven PluginのようなJavaVM引数を使うようなMavenプラグインと、Maven Surefire Plugin(Maven Failsafe Pluginも)のargLine
を
一緒に使うとちょっとハマるのでメモとして。
Maven Surefire Plugin(Maven Failsafe Plugin)のargLine
JaCoCo Maven Pluginのjacoco:prepare-agentを使うと、こんな感じでargLine
にJavaVM引数(具体的には-javaagent
)を指定します。
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ jacoco-surefire-argline --- [INFO] argLine set to -javaagent:$HOME/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/path/to/target/jacoco.exec
ここでMaven Surefire Plugin(Maven Failsafe Plugin)のargLine
オプションを指定すると、JaCoCoがうまく動かなくなります。
理由は、Maven Surefire Plugin向けに設定したargLine
がJaCoCo Maven Pluginが指定したJavaVM引数を上書きしてしまうからです。
これはよくハマる問題のようで、Maven Surefire Pluginのtestゴールにも書いてありますし
Since the Version 2.17 using an alternate syntax for argLine, @{...} allows late replacement of properties when the plugin is executed, so properties that have been modified by other plugins will be picked up correctly. See the Frequently Asked Questions page with more details:
surefire:test / Parameter Details / argLine
JaCoCo Maven Pluginのjacoco:prepare-agentゴールのドキュメントにも書いてあります。
f your project already defines VM arguments for test execution, be sure that they will include property defined by JaCoCo.
Frequently Asked Questions / How do I use properties set by other plugins in argLine?
結論としては、Maven Surefire Pluginの@{argLine}
を使って指定するのがよさそうです。
少し試しておきます。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.4 2024-07-16 OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04) OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.8 (36645f6c9b5079805ea5009217e36f2cffd34256) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-117-generic", arch: "amd64", family: "unix"
準備
Maven依存関係などはこちら。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.26.3</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> <executions> <execution> <id>prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins>
ソースコードを作成
とてもわざとらしいですが、システムプロパティを使ったソースコードとテストコードを用意します。
src/main/java/org/littlewings/jacoco/SystemPropertyService.java
package org.littlewings.jacoco; public class SystemPropertyService { public String get(String name) { return System.getProperty(name); } }
src/test/java/org/littlewings/jacoco/SystemPropertyServiceTest.java
package org.littlewings.jacoco; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; class SystemPropertyServiceTest { @Test void systemProperty() { SystemPropertyService systemPropertyService = new SystemPropertyService(); assertThat(systemPropertyService.get("message")).isNotNull(); System.out.printf("message = %s%n", systemPropertyService.get("message")); } }
システムプロパティよりも-Xmx
などの方がわかりやすい(というかそもそも使わなくてもいい)と思うのですが、テストでの見た目としては
このあたりの方がわかりやすいかなということで。
Maven Surefire PluginのargLineを使いつつJaCoCoのカバレッジレポートを取得する
まずはテストを実行してみます。
$ mvn test
システムプロパティを指定していないので、このテストは当然失敗するのですが
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.jacoco.SystemPropertyServiceTest [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.769 s <<< FAILURE! -- in org.littlewings.jacoco.SystemPropertyServiceTest [ERROR] org.littlewings.jacoco.SystemPropertyServiceTest.systemProperty -- Time elapsed: 0.636 s <<< FAILURE! java.lang.AssertionError: Expecting actual not to be null at org.littlewings.jacoco.SystemPropertyServiceTest.systemProperty(SystemPropertyServiceTest.java:12) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) [INFO] [INFO] Results: [INFO] [ERROR] Failures: [ERROR] SystemPropertyServiceTest.systemProperty:12 Expecting actual not to be null [INFO] [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
jacoco.exec
はできるのでレポートは作ることができます。
$ mvn jacoco:report
こんな感じで。
[INFO] --- jacoco:0.8.12:report (default-cli) @ jacoco-surefire-argline --- [INFO] Loading execution data file /path/to/target/jacoco.exec [INFO] Analyzed bundle 'jacoco-surefire-argline' with 1 classes
target/site/jacoco
ディレクトリ内の中身については省略します。
ではここで1度mvn clean
します。
$ mvn clean
そしてMaven Surefire PluginのargLine
にJavaVM引数を設定します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> <configuration> <argLine>-Xmx1G -Dmessage=Hello</argLine> </configuration> </plugin>
テストを実行。今度は成功します。
$ mvn test
結果。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.jacoco.SystemPropertyServiceTest message = Hello [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.243 s -- in org.littlewings.jacoco.SystemPropertyServiceTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
そしてJaCoCoのレポートを生成しようとすると
$ mvn jacoco:report
スキップされます。
[INFO] --- jacoco:0.8.12:report (default-cli) @ jacoco-surefire-argline --- [INFO] Skipping JaCoCo execution due to missing execution data file.
そもそもtarget/jacoco.exec
も生成されていません。
$ ll target/jacoco.exec ls: 'target/jacoco.exec' にアクセスできません: そのようなファイルやディレクトリはありません
JaCoCoのエージェントは組み込まれているんですけどね。
[INFO] --- jacoco:0.8.12:prepare-agent (prepare-agent) @ jacoco-surefire-argline --- [INFO] argLine set to -javaagent:$HOME/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/path/to/target/jacoco.exec
これが最初に書いた問題です。
ちなみに、Maven Surefire Pluginはこの設定のままで
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> </plugin>
-DargLine
で指定した場合でもテストは成功しますが
$ mvn test -DargLine='-Xmx1G -Dmessage=Hello'
カバレッジレポートを生成しようとすると
$ mvn jacoco:report
やっぱりスキップされます。
[INFO] --- jacoco:0.8.12:report (default-cli) @ jacoco-surefire-argline --- [INFO] Skipping JaCoCo execution due to missing execution data file.
ではどうやって回避するかというと@{argLine}
を追加します。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> <configuration> <argLine>@{argLine} -Xmx1G -Dmessage=Hello</argLine> </configuration> </plugin>
@{argLine}
には、Mavenプラグインが実行時に設定した値が入ります。
Since the Version 2.17 using an alternate syntax for argLine, @{...} allows late replacement of properties when the plugin is executed, so properties that have been modified by other plugins will be picked up correctly.
surefire:test / Parameter Details / argLine
これでテストを実行してカバレッジレポートを作成すると
$ mvn clean test jacoco:report
今度はうまくいくようになります。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.jacoco.SystemPropertyServiceTest message = Hello [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.908 s -- in org.littlewings.jacoco.SystemPropertyServiceTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- jacoco:0.8.12:report (default-cli) @ jacoco-surefire-argline --- [INFO] Loading execution data file /paht/to/target/jacoco.exec [INFO] Analyzed bundle 'jacoco-surefire-argline' with 1 classes
別解としては、Maven Surefire Pluginの設定は戻して
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> </plugin>
properties
としてargLine
を定義することですね。
<properties> ... <argLine>-Xmx1G -Dmessage=Hello</argLine> </properties>
これでもうまくいきます(jacoco:report
の実行結果は省略します)。
ただ、この方法だとコマンドラインで-DargLine
を指定するパターンとは共存できません。
$ mvn test -DargLine='-Xmx1G -Dmessage="Hello World"'
テスト自体はうまくいきますが
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.jacoco.SystemPropertyServiceTest message = Hello World [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.116 s -- in org.littlewings.jacoco.SystemPropertyServiceTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
レポートを生成しようとすると
$ mvn jacoco:report
やっぱりスキップされます。
[INFO] --- jacoco:0.8.12:report (default-cli) @ jacoco-surefire-argline --- [INFO] Skipping JaCoCo execution due to missing execution data file.
これが嫌な場合は、以下のように@{argLine}
も${argLine}
も書いておくとうまくいくようになります。
<properties> ... <argLine>-Xmx1G -Dmessage=Hello</argLine> </properties> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.3.1</version> <configuration> <argLine>@{argLine} ${argLine}</argLine> </configuration> </plugin> ... </plugins> </build>
以下のパターンのいずれでも、target/jacoco.exec
が生成されるようになります。
$ mvn test $ mvn test -DargLine='-Xmx1G -Dmessage="Hello World"'
-DargLine
を指定しなかった場合は、properties
に定義したデフォルト値で動作するという感じですね。
デフォルト値が要らないのであれば、中身は空でよいでしょう。
<properties> ... <argLine></argLine> </properties>
こんなところでしょうか。
おわりに
Maven Surefire Plugin(Maven Failsafe Plugin)とJaCoCo Maven Pluginなどを組み合わせた時に、argLine
が競合するとハマるということで
書いてみました。
知っていれば対応できるのですが、知らないと延々とハマりそうだなということでメモとして。