Mavenプラグインのユニットテストを作成するために、maven-plugin-testing-harnessというライブラリが 提供されています。ここではこのライブラリをJUnitと組み合わせて、ユニットテストを作成する方法を紹介します。

pom.xmlに依存ライブラリを追記する

まずJUnitとmaven-plugin-testing-harnessを使用するために、以下2つの<dependency>pom.xmlに追加します。

<dependencies>
...
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>{{book.version.junit}}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>

ユニットテストを作成する

追加したらユニットテストの作成を開始できます。 maven-plugin-testing-harnessではJUnit4の@Ruleアノテーションを利用しての開発が可能です。 もっとも単純なテストは次のようになります。

public class SampleMojoTest {
@Rule
public MojoRule mojoRule = new MojoRule();
@Rule
public TestResources resources = new TestResources();
@Test
public void testMojoHasHelpGoal() throws Exception {
// src/test/projects/project/pom.xml に書かれた設定を元にMojoインスタンスを作成
File baseDir = resources.getBasedir("project");
File pom = new File(baseDir, "pom.xml");
// 'help' ゴールを実行
Mojo mojo = mojoRule.lookupMojo("help", pom);
mojo.execute();
}
}

MojoRuleはMojoインスタンスを生成するためのJUnit Ruleです。

TestResourcesは各テストメソッドで固有のリソースを使うためのJUnit Ruleです。 src/test/projects次にダミーのMavenプロジェクトが入ったディレクトリを配置しておき、 そのディレクトリ名をgetBasedir()メソッドに渡すことで、ダミープロジェクトの設定を元に Mojoインスタンスを作成できます。

つまりこのテストは、ダミープロジェクトの設定を元に作成したMojoインスタンスのメソッドを呼ぶことで プラグインが正常に動作することを確認するためのものです。

期待どおりに正常終了することをテストする

前述のとおり、MojoRuleのインスタンスメソッドを通じて取得したMojoインスタンスのexecute()メソッドを呼ぶことで、 実際にMavenプラグインを実行できます。execute()メソッドが例外を投げずに終了した場合、Mavenプラグインが 正常終了したとみなせます。

@Test
public void testGoalSucceeds() {
File baseDir = resources.getBasedir("project");
File pom = new File(baseDir, "pom.xml");
Mojo samplePlugin = mojoRule.lookupMojo("help", pom);
assertNotNull(samplePlugin);
samplePlugin.execute();
}

テスト内容によっては、実行時にMojoの状態を確認するコードなどを記述する必要があります。

期待どおりに異常終了することをテストする

設定に問題があるときや必要なファイルが存在しないときは、Mavenプラグインを異常終了させる必要があります。

JUnitのテストとしては、execute()メソッドがMojoFailureExceptionあるいはMojoExecutionExceptionを投げることを確認するコードを書きます。 次のように@Testアノテーションに期待される例外を指定してください。

@Test(expected = MojoFailureException.class)
public void testGoalFailsAsExpected() {
File baseDir = resources.getBasedir("project");
File pom = new File(baseDir, "pom.xml");
Mojo samplePlugin = mojoRule.lookupMojo("help", pom);
assertNotNull(samplePlugin);
samplePlugin.execute();
}

WikiによるとMojoExecutionException設定に問題がありMojoの実行ができなかったときに、MojoFailureException依存関係やプロジェクトが持つソースコードに問題がありMojoの実行が失敗したときに投げる必要があります。

Javadocに記載されている表現で言い換えると、MojoExecutionExceptionはプラグイン提供者にとって予期しない問題が生じたときにビルドをエラー終了させるために使います。一方でMojoFailureExceptionはプラグイン提供者の想定する問題が生じたときにビルドを失敗させるために使います。 適宜使い分けてください。

ログが正しく呼ばれていることを確認する

Mavenプラグインを使用するユーザは、ログを通じてプラグインからの結果報告や異常通知などを受けます。 このためログが特定の条件下で期待どおりに出ることは、ぜひテストで確認・保証しておきたいポイントです。

Mojoインタフェースはロガーをセットするメソッドを提供していますので、モックオブジェクトを利用できます。 次のコードはMockitoを利用してデバッグログの出力内容を確認するものです。

@Test
public void testSampleGoalPrintsOutputDirectory() throws Exception {
File baseDir = resources.getBasedir("simple");
File pom = new File(baseDir, "pom.xml");
Log log = Mockito.mock(Log.class);
Mojo samplePlugin = mojoRule.lookupMojo("sample", pom);
samplePlugin.setLog(log);
samplePlugin.execute();
Mockito.verify(log).debug("outputDirectory is /tmp/target");
}

以上で紹介したように、Mavenプラグインは簡単にユニットテストによってテストできます。 複数の動作環境で動作することを保証する意味でも、ソースコードの変更によるバグ混入を未然に防ぐ意味でも、自動テストはプラグイン開発に有用です。 Jenkinsのマルチ構成プロジェクトJUnitのTheoryと組み合わせるなどして、機能の安定提供に役立ててください。