JUnit 4ルールのガイド

  • link:/category/testing/ [テスト]

  • JUnit

1. 概要

このチュートリアルでは、https://junit.org/junit4/ [JUnit 4]ライブラリによって提供されるルール機能を見ていきます。
まず、ディストリビューションで提供される最も重要な基本ルールを説明する前に、JUnitルールモデルを紹介します。 *さらに、独自のカスタムJUnitルールを作成および使用する方法も確認します。*
JUnitを使用したテストの詳細については、包括的なlink:/junit[JUnitシリーズ]をご覧ください。
  • JUnit 5を使用している場合、ルールはlink:/junit-5-extensions [拡張モデル]に置き換えられています。*

2. JUnit 4ルールの概要

  • JUnit 4ルールは、テストケースの実行を中心にいくつかのコードを実行することにより、テストを強化する柔軟なメカニズムを提供します*。 ある意味では、テストクラスに@Before and _ @ After_アノテーションを含めることに似ています。

    テストのセットアップ中にデータベースなどの外部リソースに接続し、テストの終了後に接続を閉じたいとします。 そのデータベースを複数のテストで使用する場合、すべてのテストでそのコードを複製することになります。
    *ルールを使用することで、すべてを1か所に分離し、複数のテストクラスからコードを簡単に再利用できます。*

3. JUnit 4ルールの使用

では、どのようにルールを使用できますか? 次の簡単な手順に従って、JUnit 4ルールを使用できます。
  • _public_フィールドをテストクラスに追加し、タイプが
    このフィールドは、_org.junit.rules.TestRule_インターフェースのサブタイプです

  • フィールドに_ @ Rule_アノテーションを付けます

    次のセクションでは、開始する必要があるプロジェクトの依存関係を確認します。

4. Mavenの依存関係

まず、サンプルに必要なプロジェクトの依存関係を追加しましょう。 メインのJUnit 4ライブラリのみが必要です。
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
いつものように、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22junit%22%20AND%20a%3A%22junit%22 [Maven Central]から最新バージョンを取得できます。 。

5. ディストリビューションで提供されるルール

もちろん、* JUnitは、ライブラリの一部として多数の便利な定義済みルールを提供します*。 これらのルールはすべて_org.junit.rules_パッケージで見つけることができます。
このセクションでは、それらの使用方法の例をいくつか紹介します。

* 5.1。 _TemporaryFolder_ルール*

テスト時には、多くの場合、一時ファイルまたはフォルダーにアクセスする必要があります。 ただし、これらのファイルの作成と削除の管理は面倒です。 * _TemporaryFolder_ルールを使用して、テストメソッドの終了時に削除する必要があるファイルおよびフォルダーの作成を管理できます*:
@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();

@Test
public void givenTempFolderRule_whenNewFile_thenFileIsCreated() throws IOException {
    File testFile = tmpFolder.newFile("test-file.txt");

    assertTrue("The file should have been created: ", testFile.isFile());
    assertEquals("Temp folder and test file should match: ",
      tmpFolder.getRoot(), testFile.getParentFile());
}
ご覧のとおり、最初に_TemporaryFolder_ルール_tmpFolder_を定義します。 次に、テストメソッドは、_test-file.txt_というファイルを一時フォルダーに作成します。 次に、ファイルが作成され、必要な場所に存在することを確認します。 本当に素敵でシンプル!
テストが終了したら、一時フォルダーとファイルを削除する必要があります。 *ただし、このルールは削除が成功したかどうかをチェックしません。*
このクラスで言及する価値のある他の興味深いメソッドもいくつかあります。
  • [source、actionscript3、gutter:、true]

newFile()
 ファイル名を指定しない場合、このメソッドはランダムな名前の新しいファイルを作成します。
*  [source、actionscript3、gutter:、true]
newFolder(String... folderNames)
再帰的に深い一時フォルダーを作成するには、このメソッドを使用できます。
  • [source、actionscript3、gutter:、true]

newFolder()
同様に、_newFolder()_メソッドは、ランダムな名前の新しいフォルダーを作成します。
言及する価値のある追加機能は、バージョン4.13以降、_TemporaryFolder_ルールによって削除されたリソースの検証が許可されることです。
@Rule
public TemporaryFolder folder = TemporaryFolder.builder().assureDeletion().build();
リソースを削除できない場合、テストは_AssertionError_で失敗します。
最後に、https://www.baeldung.com/junit-5 [JUnit 5]では、https://www.baeldung.com/junit-5-temporary-directory [Temporary Directory extension]を使用して同じ機能を実現できます。 ]。

* 5.2。 _ExpectedException_ルール*

名前が示すように、* _ ExpectedException_ルールを使用して、一部のコードが予想されるlink:/java-exceptions[exception]:*をスローすることを確認できます。
@Rule
public final ExpectedException thrown = ExpectedException.none();

@Test
public void givenIllegalArgument_whenExceptionThrown_MessageAndCauseMatches() {
    thrown.expect(IllegalArgumentException.class);
    thrown.expectCause(isA(NullPointerException.class));
    thrown.expectMessage("This is illegal");

    throw new IllegalArgumentException("This is illegal", new NullPointerException());
}
上記の例でわかるように、最初に_ExpectedException_ルールを宣言しています。 次に、テストでは、_IllegalArgumentException_がスローされることをアサートしています。
*このルールを使用して、メッセージや原因など、例外の他のプロパティも確認できます。*
JUnitで例外をテストするための詳細なガイドについては、https://www.baeldung.com/junit-assert-exception [例外のアサート]の方法に関する優れたガイドをご覧ください。

* 5.3。 _TestName_ルール*

*簡単に言うと、_TestName_ルールは、特定のテストメソッド内の現在のテスト名を提供します。*
@Rule public TestName name = new TestName();

@Test
public void givenAddition_whenPrintingTestName_thenTestNameIsDisplayed() {
    LOG.info("Executing: {}", name.getMethodName());
    assertEquals("givenAddition_whenPrintingTestName_thenTestNameIsDisplayed", name.getMethodName());
}
この簡単な例では、単体テストを実行すると、出力にテスト名が表示されるはずです。
INFO  c.baeldung.rules.JUnitRulesUnitTest -
    Executing: givenAddition_whenPrintingTestName_thenTestNameIsDisplayed

* 5.4。 _Timeout_ルール*

次の例では、_Timeout_ルールを見ていきます。 *このルールは、個々のテストアノテーションでタイムアウトパラメータを使用するための便利な代替手段を提供します*。
次に、このルールを使用して、テストクラスのすべてのテストメソッドにグローバルタイムアウトを設定する方法を見てみましょう。
@Rule
public Timeout globalTimeout = Timeout.seconds(10);

@Test
public void givenLongRunningTest_whenTimout_thenTestFails() throws InterruptedException {
    TimeUnit.SECONDS.sleep(20);
}
上記の簡単な例では、*最初にすべてのテストメソッドのグローバルタイムアウトを10秒に定義します*。 次に、10秒以上かかるテストを意図的に定義します。
このテストを実行すると、テストの失敗が表示されるはずです。
org.junit.runners.model.TestTimedOutException: test timed out after 10 seconds
...

* 5.5。 _ErrorCollector_ルール*

次に、_ErrorCollector_ルールを見ていきます。 *このルールにより、最初の問題が見つかった後もテストの実行を継続できます*。
このルールを使用してすべてのエラーを収集し、テストが終了したときに一度にすべてを報告する方法を見てみましょう。
@Rule
public final ErrorCollector errorCollector = new ErrorCollector();

@Test
public void givenMultipleErrors_whenTestRuns_thenCollectorReportsErrors() {
    errorCollector.addError(new Throwable("First thing went wrong!"));
    errorCollector.addError(new Throwable("Another thing went wrong!"));

    errorCollector.checkThat("Hello World", not(containsString("ERROR!")));
}
上記の例では、コレクターに2つのエラーを追加します。 *テストを実行すると、実行は継続されますが、テストは最後に失敗します。*
出力では、両方のエラーが報告されます:
java.lang.Throwable: First thing went wrong!
...
java.lang.Throwable: Another thing went wrong!

* 5.6。 _Verifier_ルール*

  • _Verifier_ルールは、テストからいくつかの追加の動作を検証したいときに使用できる抽象基本クラスです*。 実際、前のセクションで見た_ErrorCollector_ルールはこのクラスを拡張します。

    ここで、独自の検証を定義する簡単な例を見てみましょう。
private List messageLog = new ArrayList();

@Rule
public Verifier verifier = new Verifier() {
    @Override
    public void verify() {
        assertFalse("Message Log is not Empty!", messageLog.isEmpty());
    }
};
ここでは、新しい_Verifier_を定義し、_verify()_メソッドをオーバーライドして、追加の検証ロジックを追加します。 この簡単な例では、この例のメッセージログが空でないことを確認するだけです。
これで、単体テストを実行してメッセージを追加すると、検証が適用されたことがわかります。
@Test
public void givenNewMessage_whenVerified_thenMessageLogNotEmpty() {
    // ...
    messageLog.add("There is a new message!");
}

* 5.7。 _DisableOnDebug_ルール*

*デバッグ中にルールを無効にしたい場合があります*。 たとえば、デバッグの際に_Timeout_ルールを無効にして、適切にデバッグする前にテストのタイムアウトと失敗を回避することが望ましい場合がよくあります。
_DisableOnDebug_ルールはこれを正確に行い、デバッグ時に無効にする特定のルールにラベルを付けることができます。
@Rule
public DisableOnDebug disableTimeout = new DisableOnDebug(Timeout.seconds(30));
上記の例では、このルールを使用するために、*無効にするルールをコンストラクターに渡すだけです*。
このルールの主な利点は、デバッグ中にテストクラスを変更せずにルールを無効にできることです。

* 5.8。 _ExternalResource_ルール*

通常、統合テストを作成するときは、テストの前に外部リソースをセットアップし、後でそれを破棄することをお勧めします。 ありがたいことに、JUnitはこのための別の便利な基本クラスを提供します。
*ファイルやデータベース接続など、テストの前に外部リソースを設定するために、抽象クラス_ExternalResource_を拡張できます。*実際、先ほど見た_TemporaryFolder_ルールは_ExternalResource_を拡張します。
このクラスを拡張する方法を簡単に見てみましょう。
@Rule
public final ExternalResource externalResource = new ExternalResource() {
    @Override
    protected void before() throws Throwable {
        // code to set up a specific external resource.
    };

    @Override
    protected void after() {
        // code to tear down the external resource
    };
};
この例では、外部リソースを定義するとき、外部リソースを設定および破棄するために、_before()_メソッドおよび_after()_メソッドをオーバーライドするだけです。

6. クラスルールの適用

これまで、これまで見てきたすべての例は、単一のテストケースメソッドに適用されてきました。 *ただし、テストクラスレベルでルールを適用する場合があります*。 _ @ ClassRule_アノテーションを使用してこれを実現できます。
この注釈は_ @ Rule_と非常によく似ていますが、テスト全体をルールでラップします。*主な違いは、クラスルールに使用するフィールドが静的でなければならないことです*。
@ClassRule
public static TemporaryFolder globalFolder = new TemporaryFolder();

7. カスタムJUnitルールの定義

これまで見てきたように、JUnit 4はすぐに使える便利なルールを数多く提供しています。 もちろん、独自のカスタムルールを定義できます。 *カスタムルールを作成するには、_TestRule_インターフェイスを実装する必要があります。*
カスタムテストメソッド名ロガールールを定義する例を見てみましょう。
public class TestMethodNameLogger implements TestRule {

    private static final Logger LOG = LoggerFactory.getLogger(TestMethodNameLogger.class);

    @Override
    public Statement apply(Statement base, Description description) {
        logInfo("Before test", description);
        try {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    base.evaluate();
                }
            };
        } finally {
            logInfo("After test", description);
        }
    }

    private void logInfo(String msg, Description description) {
        LOG.info(msg + description.getMethodName());
    }
}
ご覧のとおり、_TestRule_インターフェイスには、_Statement_のインスタンスを返すためにオーバーライドする必要がある_apply(Statement、Description)_という1つのメソッドが含まれています。 このステートメントは、JUnitランタイム内のテストを表します。 * _evaluate()_メソッドを呼び出すと、テストが実行されます。*
この例では、前後のメッセージを記録し、_Description_オブジェクトから個々のテストのメソッド名を含めます。

8. ルールチェーンの使用

*この最後のセクションでは、_RuleChain_ルールを使用していくつかのテストルールを注文する方法を見ていきます。*
@Rule
public RuleChain chain = RuleChain.outerRule(new MessageLogger("First rule"))
    .around(new MessageLogger("Second rule"))
    .around(new MessageLogger("Third rule"));
上記の例では、各_MessageLogger_コンストラクターに渡されるメッセージを単純に出力する3つのルールのチェーンを作成します。
テストを実行すると、チェーンがどのように順番に適用されるかがわかります。
Starting: First rule
Starting: Second rule
Starting: Third rule
Finished: Third rule
Finished: Second rule
Finished: First rule

9. 結論

要約すると、このチュートリアルでは、JUnit 4ルールを詳細に調査しました。
まず、ルールとは何か、どのように使用できるかを説明することから始めました。 次に、JUnitディストリビューションの一部として提供されるルールを詳しく調べました。
最後に、独自のカスタムルールを定義する方法と、ルールを連結する方法を検討しました。
いつものように、記事の完全なソースコードが利用可能ですgithttps://github.com/eugenp/tutorials/tree/master/testing-modules/junit-4 [over on GitHub]。