ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Maven の Surefire プラグインと JMockit と JaCoCo プラグイン

ビルドツールとして Maven を使っている Java プロジェクトで JMockit と JaCoCo を使いたいときの話。 ユニットテストの実行には Maven Surefire プラグインを使用しているものとする。 また、JaCoCo の適用には JaCoCo Maven プラグインを使用するものとする。

JMockit と JaCoCo の -javaagent 指定を共存させる

JMockit も JaCoCo も、Java プログラミング言語エージェントの仕組みを利用している *1。 なので、どちらも基本的には -javaagent の指定が必要である。

JMockit-javaagent 指定

JMockit の方は、Surefire プラグインargLine パラメータで -javaagent を指定する。

<plugins>
   <plugin>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.21.0</version> <!-- or some other version -->
      <configuration>
         <argLine>
            -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
         </argLine>
         <!-- ... -->
      </configuration>
   <plugin>
   <!-- ... -->
<plugins>
JMockit - Tutorial - Introduction

JaCoCo の -javaagent 指定

JaCoCo については jacoco:prepare-agent ゴールにて argLine プロパティ *2-javaagent が追加される仕組みになっている。

Surefire プラグインargLine パラメータが指定されていないときには argLine プロパティの値が使われるので、Surefire プラグインargLine パラメータが指定されていない場合にはそれ以上の設定は必要ない。

一方で、Surefire プラグインargLine パラメータの指定がされている場合は (そのままでは) JaCoCo プラグインが設定した argLine プロパティの値が使われないため、別途設定が必要となる。 具体的には、Surefire プラグインlate property evaluation (late replacement) を使用する。 これは、プロパティ参照の ${...} の代わりに @{...} を使うというもの。 ${...} を使った場合はプラグインの実行前に評価されるため JaCoCo プラグインargLine プロパティを変更してもそれが反映されないが、@{...} を使うとプラグインの実行後の値が反映される。

One of the ways to do this in case of maven-surefire-plugin - is to use syntax for late property evaluation:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
      <argLine>@{argLine} -your -extra -arguments</argLine>
    </configuration>
  </plugin>

IntelliJ IDEA での問題

さて、Maven でビルドを実行する際に JMockit と JaCoCo を共存させるだけならここまでで解決するが、さらにややこしいのが IntelliJ IDEA の存在である。

IntelliJ IDEA で Maven プロジェクトを扱う場合、デフォルトでは JUnit 実行時に Surefire プラグインargLine パラメータを読み取るのだが、IntelliJ IDEA は late replacement (@{...}) に対応していない。 そのため、下記のようなエラーが発生してしまう。

エラー: メイン・クラス@{argLine}が見つからなかったかロードできませんでした

回避策の一つは、IntelliJ IDEA でのユニットテスト実行時に Surefire プラグインargLine パラメータを参照する設定を無効にすること。 設定の中の 「Maven」 の 「Running Tests」 で Surefire プラグインargLine を参照するかどうか指定できる。

私が採った回避策は、argLine パラメータには空文字列として宣言したプロパティを含めておき、JaCoCo を有効にしたいときだけコマンドライン引数で @{argLine} を埋め込むというもの。

    <properties>
        <!--
        Surefire プラグインの `argLine` に含める文字列。 JaCoCo の Java Agent を有効にするためには `@{argLine}` を指定すること。
        IntelliJ IDEA が `@{...}` による late replacement に対応しておらず、直接 `@{argLine}` を指定すると IntelliJ IDEA
        でのテスト実行時にエラーが発生してしまうので、`@{argLine}` をコマンドラインオプションで指定できるように
        このようなプロパティを用意した。
        See : http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html (Surefire Plugin の `argLine` の説明)
        See : https://www.eclemma.org/jacoco/trunk/doc/prepare-agent-mojo.html (JaCoCo の Java Agent の指定について)
        See : https://youtrack.jetbrains.com/issue/IDEA-143187 (IntelliJ IDEA が argLine の `@{...}` を解釈しない件)
        -->
        <surefireArgLine/>
    </properties>
    <!-- (中略) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.19.1</version>
                <configuration>
                    <argLine>
                        ${surefireArgLine}
                        <!-- See : http://jmockit.github.io/tutorial/Introduction.html#runningTests -->
                        -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
                    </argLine>

上のようにしておけば IntelliJ IDEA でのテスト実行にエラーが出ることはない。 そして Mavenコマンドラインから動かすときには、下記のように指定すれば JaCoCo が有効になる。

mvn package -D surefireArgLine="@{argLine}"

もっといい方法があれば知りたい……。

*1:JaCoCo についてはオフラインインストゥルメントの仕組みもあるが。

*2:デフォルトの場合。 変更可。