1. 概要

単体テストでは、定期的に、テストメソッドの実行結果を処理したい場合があります。 このクイックチュートリアルでは、JUnitが提供するTestWatcherAPIを使用してこれを実現する方法を見ていきます。

JUnitを使用したテストの詳細なガイドについては、優れた JUnit5ガイドをご覧ください。

2. TestWatcher API

要するに、TestWatcherインターフェースは、テスト結果を処理したい拡張機能のAPIを定義します。 このAPIを考える1つの方法は、個々のテストケースのステータスを取得するためのフックを提供することです。

しかし、いくつかの実際の例に飛び込む前に、一歩下がって、TestWatcherインターフェースのメソッドを簡単に要約しましょう。

  • testAborted​(ExtensionContext context, Throwable cause)

    中止されたテストの結果を処理するために、testAbortedメソッドをオーバーライドできます。 名前が示すように、このメソッドはテストが中止された後に呼び出されます。

  • testDisabled​(ExtensionContext context, Optional reason)

    無効にしたテストメソッドの結果を処理する場合は、testDisabledメソッドをオーバーライドできます。 この方法には、テストが無効になっている理由も含まれる場合があります。

  • testFailed(ExtensionContext context, Throwable cause)

    テストが失敗した後に追加の処理を実行する場合は、testFailedメソッドで機能を実装するだけです。 この方法には、テストの失敗の原因が含まれている場合があります。

  • testSuccessful(ExtensionContext context)

    最後になりましたが、成功したテストの結果を処理する場合は、testSuccessfulメソッドをオーバーライドするだけです。

すべてのメソッドにExtensionContextが含まれていることに注意してください。 これにより、現在のテストが実行されたコンテキストがカプセル化されます。

3. Mavenの依存関係

まず、例に必要なプロジェクトの依存関係を追加しましょう。 メインのJUnit5ライブラリjunit-jupiter-engineとは別に、junit-jupiter-apiライブラリも必要です。

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.8.1</version>
    <scope>test</scope>
</dependency>

いつものように、 MavenCentralから最新バージョンを入手できます。

4. TestResultLoggerExtensionの例

TestWatcher APIの基本を理解したので、実際の例を見ていきます。

結果をログに記録し、テストの要約を提供するための簡単な拡張機能を作成することから始めましょう。 この場合、拡張機能を作成するには、TestWatcherインターフェイスを実装するクラスを定義する必要があります。

public class TestResultLoggerExtension implements TestWatcher, AfterAllCallback {
    private List<TestResultStatus> testResultsStatus = new ArrayList<>();

    private enum TestResultStatus {
        SUCCESSFUL, ABORTED, FAILED, DISABLED;
    }

    //...
}

すべての拡張インターフェイスと同様に、TestWatcherインターフェイスもメインの拡張インターフェイスを拡張します。これは、マーカーインターフェイスのみです。 この例では、AfterAllCallbackインターフェースも実装しています。

拡張機能には、 TestResultStatus のリストがあります。これは、テスト結果のステータスを表すために使用する単純な列挙です。

4.1. テスト結果の処理

それでは、個々の単体テストメソッドの結果を処理する方法を見てみましょう。

@Override
public void testDisabled(ExtensionContext context, Optional<String> reason) {
    LOG.info("Test Disabled for test {}: with reason :- {}", 
      context.getDisplayName(),
      reason.orElse("No reason"));

    testResultsStatus.add(TestResultStatus.DISABLED);
}

@Override
public void testSuccessful(ExtensionContext context) {
    LOG.info("Test Successful for test {}: ", context.getDisplayName());

    testResultsStatus.add(TestResultStatus.SUCCESSFUL);
}  

まず、拡張機能の本体を埋め、 testDisabled()メソッドとtestSuccessful()メソッドをオーバーライドします。

簡単な例では、テストの名前を出力し、テストのステータスをtestResultsStatusリストに追加します。

testAborted() testFailed()の2つのメソッドについても、この方法で続行します。

@Override
public void testAborted(ExtensionContext context, Throwable cause) {
    LOG.info("Test Aborted for test {}: ", context.getDisplayName());

    testResultsStatus.add(TestResultStatus.ABORTED);
}

@Override
public void testFailed(ExtensionContext context, Throwable cause) {
    LOG.info("Test Failed for test {}: ", context.getDisplayName());

    testResultsStatus.add(TestResultStatus.FAILED);
}

4.2. テスト結果の要約

例の最後の部分では、 afterAll()メソッドをオーバーライドします。

@Override
public void afterAll(ExtensionContext context) throws Exception {
    Map<TestResultStatus, Long> summary = testResultsStatus.stream()
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

    LOG.info("Test result summary for {} {}", context.getDisplayName(), summary.toString());
}

簡単に要約すると、すべてのテストメソッドが実行された後にafterAllメソッドが実行されます。 このメソッドを使用して、非常に基本的な要約を出力する前に、テスト結果のリストにあるさまざまなTestResultStatusをグループ化します。

ライフサイクルコールバックの詳細なガイドについては、優れたJUnit5拡張機能のガイドをご覧ください。

5. テストの実行

この最後から2番目のセクションでは、単純なロギング拡張機能を使用して、テストからの出力がどのようになるかを確認します。

拡張機能を定義したので、最初に標準の@ExtendWithアノテーションを使用して拡張機能を登録します。

@ExtendWith(TestResultLoggerExtension.class)
class TestWatcherAPIUnitTest {

    @Test
    void givenFalseIsTrue_whenTestAbortedThenCaptureResult() {
        Assumptions.assumeTrue(false);
    }

    @Disabled
    @Test
    void givenTrueIsTrue_whenTestDisabledThenCaptureResult() {
        Assert.assertTrue(true);
    }

    //...

次に、テストクラスに単体テストを入力し、無効、中止、および成功したテストを組み合わせて追加します。

5.1. 出力の確認

単体テストを実行すると、各テストの出力が表示されます。

INFO  c.b.e.t.TestResultLoggerExtension - 
    Test Successful for test givenTrueIsTrue_whenTestAbortedThenCaptureResult()
...
Test result summary for TestWatcherAPIUnitTest {ABORTED=1, SUCCESSFUL=1, DISABLED=2}

もちろん、すべてのテスト方法が完了すると、要約も印刷されます。

6. ガッチャ

この最後のセクションでは、TestWatcherインターフェイスを使用するときに注意する必要があるいくつかの微妙な点を確認しましょう。

  • TestWatcher拡張機能がテストの実行に影響を与えることは許可されていません。 これは、 TestWatcherから例外がスローされた場合、実行中のテストまで伝播されないことを意味します。
  • 現在、このAPIは、@Testメソッドと@TestTemplateメソッドの結果を報告するためにのみ使用されています
  • デフォルトでは、 testDisabled メソッドに理由が指定されていない場合、テストメソッドの完全修飾名の後に「 is@Disabled」が続きます。

7. 結論

要約すると、このチュートリアルでは、JUnit 5 TestWatcherAPIを使用してテストメソッドの実行結果を処理する方法を示しました。

例の完全なソースコードは、GitHubにあります。