1. 概要

このドキュメントの目的は、テストを書くプログラマや機能を拡張する開発者(extension authors)、 駆動部分の開発者(engine authors)ならびにビルドツールやIDEのベンダーのために、包括的な参考資料を提供することです。

このドキュメントは PDF でも利用可能です。

Translations

このドキュメントは 簡体字中国語 でも利用可能です。

1.1. JUnit 5とは?

これまでのバージョンのJUnitとは異なり、JUnit 5は3つのサブプロジェクトで 開発されている様々なモジュールから構成されています。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform は、JVM上で テスティングフレームワークを起動させる ための基礎としての機能を果たします。 また、プラットフォーム上で動作するテスティングフレームワークを開発するための TestEngine APIを定義しています。 さらに、プラットフォーム上であらゆる TestEngine を実行するために、 JUnit 4ベースのRuunner と同様に、 コマンドラインからプラットフォームを立ち上たり、 GradleMaven 向けプラグインを構築するためのコンソールラウンチャーを提供します。

JUnit Jupiter は、JUnit 5でテストを書いたり拡張するための 新しい プログラミングモデル拡張モデルの組み合わせです。 Jupiterのサブプロジェクトは、プラットフォーム上でJupiterベースのテストを実行するための TestEngine を提供します。

JUnit Vintage は、プラットフォーム上でJUnit3とJUnit4ベースのテストを実行するための TestEngine を提供します。

1.2. サポートしているJavaのバージョン

JUnit 5は実行時にJava 8(またはそれ以上)を必要とします。しかしながら、それよりも前のバージョンのJDKでコンパイルされたコードもテスト可能です。

1.3. ヘルプ

JUnit 5に関連した質問は Stack Overflow に投稿するか、 Gitter でチャットしてください。

2. インストール

最終的なリリースやマイルストーンのためのアーティファクトは、Maven Centralにデプロイされます。

スナップショット版はSonatypeの スナップショットレポジトリ/org/junit 以下にデプロイされます。

2.1. 依存関係のメタデータ

2.1.1. JUnit Platform

  • Group ID: org.junit.platrform

  • Version: 1.2.0

  • Articfact IDs:

    • junit-platform-commons: JUnitの内部共通ライブラリ・ユーティリティ。これらのユーティリティは、JUnitフレームワーク内部使用のみが意図されています。 外部パーティからのいかなる使用はサポートされていません。 使用は自身の責任で行ってください!

    • junit-platform-console: コンソールからJUnit Platform上でテストを発見し実行することをサポートします。 詳細については Console Launcher をご覧ください。

    • junit-platform-console-standalone: 全ての依存関係が包含された実行可能な JAR ファイルが Maven Central の junit-platform-standalone 以下のディレクトリで提供されています。 詳細については Console Launcher をご覧ください。

    • junit-platform-engine: テストエンジンのパブリックAPIです。 詳細については 自分自身のテストエンジンをプラグインする をご覧ください。

    • junit-platform-gradle-pluginGralde を用いたJUnit Platform上の テストの発見・実行をサポートします。

    • junit-platform-launcher: テストプランの設定・起動するためのパブリックAPIです。 典型的にはIDEやビルドツールによって使われます。 詳細については JUnit Platform Launcher API をご覧ください。

    • junit-platform-runner: JUnit 4環境におけるJUnit Platform上でテスト・テストスイートを実行するためのランナーです。 詳細についてはJUnit Platformを実行するためにJUnit 4を使用する をご覧ください。

    • junit-platform-suite-api: JUnit Platform上でテストスイートを設定をするためのアノテーションです。 JUnitPlatform runner にサポートされています。 また、サードパーティによる TestEngine 実装にもサポートされている可能性があります。

    • junit-platform-surefire-providerMaven Surefire を用いたJUnit Platform上のテストの発見・実行をサポートします。

2.1.2. JUnit Jupiter

  • Group ID: org.junit.jupiter

  • Version: 5.2.0

  • Articfact IDs:

    • junit-jupiter-apiテストを書いたり拡張する ためのJUnit Jupiter APIです。

    • junit-jupiter-engine: JUnit Jupiter テストエンジンの実装です。実行時のみ必要です。

    • junit-jupiter-params: JUnit Jupiterでの パラメータ化テスト をサポートします。

    • junit-jupiter-migrationsupport: JUnit 4からJUnit Jupiterへのマイグレーションをサポートします。 いくつかのJUnit 4 rulesを実行する時のみ必要です。

2.1.3. JUnit Vintage

  • Group ID: org.junit.vintage

  • Version: 5.2.0

  • Articfact ID:

    • junit-vintage-engine: JUnit Vintageテストエンジンの実装です。 かつてのJUnitテスト(つまり、JUnit 3やJUnit 4形式で書かれたテスト)を新しいJUnit Platform上で実行できます。

2.1.4. 部品表

次のMaven座標以下で提供されている 部品表 POMは、 MavenGradle を用いて複数の上記アーティファクトを参照する際に、依存関係管理を容易にするために利用できます。

  • Group ID: org.junit

  • Articfact ID: junit-bom

  • Version: 5.2.0

2.1.5. 依存関係

上記のアーティファクトは全て、次の @API Guardian JAR で公開されているMaven POMsに依存関係があります。 - Group ID: org.apiguardian - Articfact ID: apiguardian-api - Version: {apiguardian-version}

さらに、上記アーティファクトのほとんどは、次の OpenTest4J JARと直接的、または推移的な依存関係があります。

  • Group ID: org.opentest4j

  • Articfact ID: opentest4j

  • Version: 5.2.0

2.2. 依存関係図

Component Diagram

2.3. JUnit Jupiter サンプルプロジェクト

junit5-samples レポジトリは、 JUnit JupiterとJUnit Vintageをベースとしたサンプルプロジェクト集をホストしています。 下記プロジェクトには build.gradlepom.xml がそれぞれ置かれています。

3. テストを書く

A first test case
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

class FirstJUnit5Tests {

    @Test
    void myFirstTest() {
        assertEquals(2, 1 + 1);
    }

}

3.1. アノテーション

JUnit Jupiterは、テストの設定やフレームワークの拡張のために、 次のアノテーションをサポートしています。

全てのコアなアノテーションは、 junit-jupiter-api モジュール内の org.junit.jupiter.api パッケージにあります。

アノテーション 説明

@Test

テストメソッドであることを意味します。JUnit 4の @Test と異なり、このアノテーションはいかなる属性も宣言しません。これは、JUnit Jupiterにおけるテスト拡張が専用のアノテーションを元に作動するからです。メソッドは オーバーライド されない限り、 継承 されます。

@ParameterizedTest

パラメータ化テスト であることを意味します。メソッドは オーバーライド されない限り、 継承 されます。

@RepeatedTest

繰り返しテスト のテンプレートメソッドであることを意味します。メソッドは オーバーライド されない限り、 継承 されます。

@TestFactory

動的テスト のファクトリーメソッドであることを意味します。メソッドは オーバーライド されない限り、 継承 されます。

@TestInstance

アノテーションが付与されたテストクラスの テストインスタンス・ライフサイクル を設定するために使用されます。アノテーションは 継承 されます。

@TestTemplate

テストケースのテンプレート メソッドであることを意味します。テンプレートは登録された プロバイダ によって返される呼び出しコンテキストの数に応じて複数回呼び出されます。メソッドは オーバーライド されない限り、 継承 されます。

@DisplayName

テストクラスもしくはテストメソッドのカスタム表示名を宣言します。アノテーションは 継承 されません。

@BeforeEach

現在のクラス内にある 各テスト@Test , @RepeatedTest , @ParameterizedTest , @TestFactory )が実行される before )に実行されるメソッドを意味します。JUnit 4の @Before と類似したものです。メソッドは オーバーライド されない限り、 継承 されます。

@AfterEach

現在のクラス内にある 各テスト@Test , @RepeatedTest , @ParameterizedTest , @TestFactory )が実行された after )に実行されるメソッドを意味します。JUnit 4の @After と類似したものです。メソッドは オーバーライド されない限り、 継承 されます。

@BeforeAll

現在のクラス内にある 全テスト@Test , @RepeatedTest , @ParameterizedTest , @TestFactory )が実行される before )に実行されるメソッドを意味します。JUnit 4の @BeforeClass と類似したものです。メソッドは 隠蔽 または オーバーライド されない限り、 継承 され、("クラス毎の" テストインスタンス・ライフサイクル を使わない限り) static でないといけません。

@AfterAll

現在のクラス内にある 全テスト@Test , @RepeatedTest , @ParameterizedTest , @TestFactory )が実行された after )に実行されるメソッドを意味します。JUnit 4の @AfterClass と類似したものです。メソッドは 隠蔽 または オーバーライド されない限り、 継承 され、("クラス毎の" テストインスタンス・ライフサイクル を使わない限り) static でないといけません。

@Nested

ネストされた非staticなテストクラスであることを意味します。 @BeforeAll@AfterAll メソッドは、"クラス毎の" テストインスタンス・ライフサイクル を使わない限り、 @Nested テストクラスの中では直接使うことができません。メソッドは 継承 されません。

@Tag

クラスもしくはメソッドレベルでテストをフィルタリングするための タグ を宣言できます。TestNGのtest groupsもしくはJUnit 4のCategoriesと類似したものです。アノテーションはクラスレベルでは 継承 されますが、メソッドレベルでは 継承 されません。

@Disabled

テストクラスもしくはテストメソッドを 無効化 できます。JUnit 4の @Ignore と類似したものです。アノテーションは 継承 されません。

@ExtendWith

カスタム 拡張 を登録できます。アノテーションは 継承 されます。

次のアノテーションをつけたメソッドは値を返してはいけません。 ( @Test , @TestTemplate , @RepeatedTest , @BeforeAll , @AfterAll , @BeforeEach , @AfterEach )

いくつかのアノテーションは現在 実験段階 である恐れがあります。 詳細に関しては、 実験的なAPIs をご覧ください。

3.1.1. メタアノテーションと合成アノテーション

JUnit Jupiterアノテーションはメタアノテーションとして使うことができます。 つまり、メタアノテーションのセマンティックを自動で 継承 する 独自の 合成アノテーション を定義できます。

例えば、コードベースに @Tag("fast")タグ付けとフィルタリング をご覧ください。)を コピー&ペーストする代わりに、次のように @Fast というカスタム 合成アノテーション を作成できます。 @Fast@Tag("fast") の代替として利用できます。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}

3.2. テストクラスとメソッド

テストメソッド とは、直接もしくはメタ的に @Test または @RepeatedTest@ParamterizedTest@TsetFactory@TestTemplate が 付与されたインスタンスメソッドです。 テストクラス とは、少なくとも1つのテストメソッドを含むトップレベルまたは静的なメンバークラスです。

標準的なテストケース
import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

    @BeforeAll
    static void initAll() {
    }

    @BeforeEach
    void init() {
    }

    @Test
    void succeedingTest() {
    }

    @Test
    void failingTest() {
        fail("a failing test");
    }

    @Test
    @Disabled("for demonstration purposes")
    void skippedTest() {
        // not executed
    }

    @AfterEach
    void tearDown() {
    }

    @AfterAll
    static void tearDownAll() {
    }

}

テストクラスもテストメソッドも public である必要はありません。

3.3. 表示名

テストクラスとテストメソッドはカスタム表示名(スペースや特殊文字、絵文字も使用可能です) を宣言できます。 それらがテストランナーとテストレポートによって表示されます。

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("A special test case")
class DisplayNameDemo {

    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

    @Test
    @DisplayName("╯°□°)╯")
    void testWithDisplayNameContainingSpecialCharacters() {
    }

    @Test
    @DisplayName("😱")
    void testWithDisplayNameContainingEmoji() {
    }

}

3.4. アサーション

JUnit Jupiterには、JUnit 4のアサーションメソッドの多くを備えています。 また、いくつかはJava 8のラムダ式で使うことができます。 全てのJUnit Jupiterアサーションは、 org.junit.jupiter.api.Assertions クラスの static メソッドです。

import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class AssertionsDemo {

    @Test
    void standardAssertions() {
        assertEquals(2, 2);
        assertEquals(4, 4, "The optional assertion message is now the last parameter.");
        assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
                + "to avoid constructing complex messages unnecessarily.");
    }

    @Test
    void groupedAssertions() {
        // In a grouped assertion all assertions are executed, and any
        // failures will be reported together.
        assertAll("person",
            () -> assertEquals("John", person.getFirstName()),
            () -> assertEquals("Doe", person.getLastName())
        );
    }

    @Test
    void dependentAssertions() {
        // Within a code block, if an assertion fails the
        // subsequent code in the same block will be skipped.
        assertAll("properties",
            () -> {
                String firstName = person.getFirstName();
                assertNotNull(firstName);

                // Executed only if the previous assertion is valid.
                assertAll("first name",
                    () -> assertTrue(firstName.startsWith("J")),
                    () -> assertTrue(firstName.endsWith("n"))
                );
            },
            () -> {
                // Grouped assertion, so processed independently
                // of results of first name assertions.
                String lastName = person.getLastName();
                assertNotNull(lastName);

                // Executed only if the previous assertion is valid.
                assertAll("last name",
                    () -> assertTrue(lastName.startsWith("D")),
                    () -> assertTrue(lastName.endsWith("e"))
                );
            }
        );
    }

    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    @Test
    void timeoutNotExceeded() {
        // The following assertion succeeds.
        assertTimeout(ofMinutes(2), () -> {
            // Perform task that takes less than 2 minutes.
        });
    }

    @Test
    void timeoutNotExceededWithResult() {
        // The following assertion succeeds, and returns the supplied object.
        String actualResult = assertTimeout(ofMinutes(2), () -> {
            return "a result";
        });
        assertEquals("a result", actualResult);
    }

    @Test
    void timeoutNotExceededWithMethod() {
        // The following assertion invokes a method reference and returns an object.
        String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
        assertEquals("Hello, World!", actualGreeting);
    }

    @Test
    void timeoutExceeded() {
        // The following assertion fails with an error message similar to:
        // execution exceeded timeout of 10 ms by 91 ms
        assertTimeout(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }

    @Test
    void timeoutExceededWithPreemptiveTermination() {
        // The following assertion fails with an error message similar to:
        // execution timed out after 10 ms
        assertTimeoutPreemptively(ofMillis(10), () -> {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100);
        });
    }

    private static String greeting() {
        return "Hello, World!";
    }

}

また、JUnit Jupiterのいくつかのアサーションメソッドは Kotlin で使うことができます。 全てのJUnit Jupiter Kotlinアサーションは、 org.junit.jupiter.api パッケージのトップレベル関数です。

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertAll
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.assertThrows

class AssertionsKotlinDemo {

    @Test
    fun `grouped assertions`() {
        assertAll("person",
            { assertEquals("John", person.firstName) },
            { assertEquals("Doe", person.lastName) }
        )
    }

    @Test
    fun `exception testing`() {
        val exception = assertThrows<IllegalArgumentException> ("Should throw an exception") {
            throw IllegalArgumentException("a message")
        }
        assertEquals("a message", exception.message)
    }

    @Test
    fun `assertions from a stream`() {
        assertAll(
            "people with name starting with J",
            people
                .stream()
                .map {
                    // This mapping returns Stream<() -> Unit>
                    { assertTrue(it.firstName.startsWith("J")) }
                }
        )
    }

    @Test
    fun `assertions from a collection`() {
        assertAll(
            "people with last name of Doe",
            people.map { { assertEquals("Doe", it.lastName) } }
        )
    }

}

3.4.1. サードパーティのアサーションライブラリ

JUnit Jupiterによって提供されているアサーション機能は多くのテストシナリオで十分ですが、 matchers といったより強力で追加的な機能が求められたり必要な場合があります。 そのような場合、JUnitチームは、 AssertJHamcrestTruth などといった サードパーティのアサーションライブラリの使用をお薦めします。 したがって、開発者は自由に選んだアサーションライブラリを使うことができます。

例えば、 matchers と流暢なAPI(fluent API)の組み合わせは、 アサーションをよりわかりやすく、読みやすくするために使うことができます。 しかしながら、JUnit Jupiterの org.junit.jupiter.api.Assertions クラスは、Hamcrestの Matcherを 許容しているJUnit 4の org.junit.Assert クラスにあるような assertThat() メソッドを提供していません。 代わりに、開発者はサードパーティのアサーションライブラリによって提供されているマッチャー用の組み込みサポートを使うことが奨励されています。

次の例は、JUnit JupiterのテストにおいてHamcrestから assertThat() のサポートを使う方法を説明しています。 Hamcrestライブラリがクラスパスに加えられている限り、 assertThat()is()equalTo() といった メソッドを静的にインポートできます。また、それらをテストの中で、下に示す assertWithHamcrestMatcher() のように使うことができます。

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.Test;

class HamcrestAssertionDemo {

    @Test
    void assertWithHamcrestMatcher() {
        assertThat(2 + 1, is(equalTo(3)));
    }

}

当然、JUnit 4のプログラミングモデルに基づいたレガシーテストも org.junit.Assert#assertThat を用いて継続して利用可能です。

3.5. アサンプション

JUnit Jupiterは、JUnit 4のアサンプションメソッドのサブセットを備えています。 また、いくつかはJava 8のラムダ式で使うことができます。 全てのJUnit Jupiterアサンプションは、 org.junit.jupiter.api.Assumptions クラスの static メソッドです。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;

import org.junit.jupiter.api.Test;

class AssumptionsDemo {

    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

    @Test
    void testOnlyOnDeveloperWorkstation() {
        assumeTrue("DEV".equals(System.getenv("ENV")),
            () -> "Aborting test: not on developer workstation");
        // remainder of test
    }

    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // perform these assertions only on the CI server
                assertEquals(2, 2);
            });

        // perform these assertions in all environments
        assertEquals("a string", "a string");
    }

}

3.6. テストの無効化

テストクラス全体もしくは各テストメソッドは、 @Disabled アノテーション または 条件付きテスト実行 で議論されているアノテーションの1つ、 カスタム ExecutionCondition によって 無効化 できます。

これは @Disabled テストクラスです。

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
class DisabledClassDemo {
    @Test
    void testWillBeSkipped() {
    }
}

そして、これは @Disabled テストメソッドを含むテストクラスです。

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class DisabledTestsDemo {

    @Disabled
    @Test
    void testWillBeSkipped() {
    }

    @Test
    void testWillBeExecuted() {
    }
}

3.7. 条件付きテスト実行

JUnit Jupiterの ExecutionCondition 拡張APIを用いて、 ある条件に基づいたコンテナまたはテストを プログラム的に 有効 または 無効 にできます。 そのような条件の最も単純な例は、@Disabled アノテーションをサポートしている 組み込みの DisabledCondition です( テストの無効化 をご覧ください)。 @Disabled に加えて、JUnit Jupiterは、 org.junit.jupiter.api.condition パッケージに 他のいくつかのアノテーションベースの条件もサポートしており、コンテナやテストを 宣言的に 有効 または 無効 にできます。詳細については、次章をご覧ください。

合成アノテーション

次章に列挙する 条件 アノテーションはいずれも、カスタム 合成アノテーション を作るために メタアノテーションとしても使える可能性があります。 例えば、 @EnabledOnOsのデモ にある @TestOnMac アノテーションは、 @Test@EnableOnOs を単一で再利用可能な アノテーションに組み合わせる方法を示しています。

次章に列挙する 条件 アノテーションはそれぞれ、 テストインターフェイスまたはテストクラス、テストメソッドに一度だけ宣言できます。 もし条件アノテーションがある要素に直接的か間接的、またはメタ的に複数存在する場合、 JUnitによって発見された最初のアノテーションのみ使われます (いかなる追加的なアノテーションも静かに無視されます)。 しかしながら、 org.junit.jupiter.api.condition パッケージでは、 各条件アノテーションは他の条件アノテーションと共に使われる可能性があります。

3.7.1. オペレーティングシステムに関する条件

@EnabledOnOs@DisabledOnOs アノテーションを使うことで、 特定のオペーティングシステム上でコンテナまたはテストを有効にしたり無効にできます。

@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
    // ...
}

@TestOnMac
void testOnMac() {
    // ...
}

@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
    // ...
}

@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
    // ...
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@EnabledOnOs(MAC)
@interface TestOnMac {
}

3.7.2. Java実行環境に関する条件

@EnabledOnJre@DisabledOnJre アノテーションを使うことで、 特定のバージョンのJava実行環境(JRE)上でコンテナまたはテストを有効にしたり無効にできます。

@Test
@EnabledOnJre(JAVA_8)
void onlyOnJava8() {
    // ...
}

@Test
@EnabledOnJre({ JAVA_9, JAVA_10 })
void onJava9Or10() {
    // ...
}

@Test
@DisabledOnJre(JAVA_9)
void notOnJava9() {
    // ...
}

3.7.3. システムプロパティに関する条件

@EnabledIfSystemProperty@DisabledIfSystemProperty アノテーションを使うことで、 named で指定したJVMシステムプロパティの値に応じて、 コンテナまたはテストを有効にしたり無効にできます。 matches 属性を使うことで、値は正規表現として解釈されます。

@Test
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
void onlyOn64BitArchitectures() {
    // ...
}

@Test
@DisabledIfSystemProperty(named = "ci-server", matches = "true")
void notOnCiServer() {
    // ...
}

3.7.4. 環境変数に関する条件

@EnabledIfEnvironmentVariable@DisabledIfEnvironmentVariable アノテーションを使うことで、 基礎となるオペレーティングシステムからの named で指定した環境変数の値に応じて、 コンテナまたはテストを有効にしたり無効にできます。 matches 属性を使うことで、値は正規表現として解釈されます。

@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches = "staging-server")
void onlyOnStagingServer() {
    // ...
}

@Test
@DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*")
void notOnDeveloperWorkstation() {
    // ...
}

3.7.5. スクリプトベースの条件

JUnit Jupiterは、 @EnabledIf@DisabledIf アノテーションを使うことで、 設定されたスクリプトの評価値に応じて、コンテナまたはテストを有効にしたり無効にできる機能を提供しています。 スクリプトは、JavaScriptまたはGroovy、 JSR 223で定義されているJava Scripting APIをサポートしているスクリプト言語であれば記述できます。

@EnabledIf@DisabledIf を使った条件付きテストテスト実行は、 現在 実験的な 機能です。 詳細については、 実験的な APIs をご覧ください。
スクリプトのロジックが、現オペレーティングまたは現Java実行環境のバージョン、 特定のJVMシステムプロパティ、特定の環境変数にのみ依存している場合、 その目的に合った組み込みのアノテーションを使うことを考慮すべきです。 さらなる詳細については、前章をご覧ください。
同じスクリプトベースの条件を多数使っている場合、より速く、型安全で、 メンテナンスのしやすい方法で条件を実装するために、それに合った ExecutionCondition 拡張を書くことを考えてみてください。
@Test // Static JavaScript expression.
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
    // ...
}

@RepeatedTest(10) // Dynamic JavaScript expression.
@DisabledIf("Math.random() < 0.314159")
void mightNotBeExecuted() {
    // ...
}

@Test // Regular expression testing bound system property.
@DisabledIf("/32/.test(systemProperty.get('os.arch'))")
void disabledOn32BitArchitectures() {
    assertFalse(System.getProperty("os.arch").contains("32"));
}

@Test
@EnabledIf("'CI' == systemEnvironment.get('ENV')")
void onlyOnCiServer() {
    assertTrue("CI".equals(System.getenv("ENV")));
}

@Test // Multi-line script, custom engine name and custom reason.
@EnabledIf(value = {
                "load('nashorn:mozilla_compat.js')",
                "importPackage(java.time)",
                "",
                "var today = LocalDate.now()",
                "var tomorrow = today.plusDays(1)",
                "tomorrow.isAfter(today)"
            },
            engine = "nashorn",
            reason = "Self-fulfilling: {result}")
void theDayAfterTomorrow() {
    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plusDays(1);
    assertTrue(tomorrow.isAfter(today));
}
スクリプトバインディング

次の名前は、各スクリプトコンテキストでバインドされているため、スクリプト内で使用可能です。 accessor は、単純な String get(String name) メソッドを介してマップライク(map-like)な構造へのアクセスを提供します。

Name Type Description

systemEnvironment

accessor

オペレーティングシステム環境変数のアクセサ

systemProperty

accessor

JVMシステムプロパティのアクセサ

junitConfigurationParameter

accessor

設定パラメータのアクセサ

junitDisplayName

String

テストまたはコンテナの表示名

junitTags

Set<String>

テストまたはコンテナに振られている全てのタグ

junitUniqueId

String

テストまたはコンテナのユニークなID

3.8. タグとフィルタリング

テストクラスとメソッドは @Tag アノテーションを用いてタグ付けできます。 それらのタグは後に テスト発見と実行 をフィルタリングするために使われます。

3.8.1. タグの構文規則

  • タグは null であってはならない。

  • トリミングされた タグは空白文字を含んではならない。

  • トリミングされた タグはISO制御文字を含んではならない。

  • トリミングされた タグは次の 予約語 のいずれも含んではならない。

    • ,:カンマ

    • (:左カッコ

    • ):右カッコ

    • &:アンパサンド

    • |:縦棒

    • !:エクスクラメーション

上の文章で、 トリミングされた というのは、 語頭と語尾の空白文字を取り除いたということを意味します。
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("fast")
@Tag("model")
class TaggingDemo {

    @Test
    @Tag("taxes")
    void testingTaxCalculation() {
    }

}

3.9. テストインスタンス・ライフサイクル

各テストメソッドの独立した実行と、変化可能なテストインスタンスの状態による 予期せぬ副作用を避けるため、JUnitは各 テストメソッド を実行する前に、 各テストクラスの新しいインスタンスを生成します ( テストクラスとメソッド をご覧ください)。 この"メソッドごと"のテストインスタンス・ライフサイクルはJUnit Jupiterでは デフォルトの動作で、以前の全てのバージョンのJUnitと類似したものになっています。

@Disabled@DisabledOnOs といった 条件 によって 無効化 された テストメソッド であっても、テストクラスはインスタンス化されることに注意してください。 これは"メソッドごと"テストインスタンス・ライフサイクルモードが有効である時でも同様です。

JUnit Jupiterに全テストメソッドを同じテストインスタンス上で実行してほしい場合は、 単にテストクラスに @TestInstance(Lifecycle.PER_CLASS) アノテーションを付与するだけで 実現可能です。このモードを使用する場合、テストクラス毎に新しいテストインスタンスが一度だけ生成されます。 これによって、テストメソッドがインスタンス変数に保存された状態に依存する場合は、 @BeforeEach または @AfterEach メソッドでその状態をリセットする必要があるかもしれません。

”クラスごと”のモードは、デフォルトの"メソッドごと"モードに比べていくつかの追加的な利点があります。 特に、"クラスごと"モードを使うと、インターフェイスの default メソッドと同様に、 @BeforeAll@AfterAll メソッドを非静的メソッドとして宣言することが可能になります。 そのため、"クラスごと"モードでは、 @Nested テストクラス内で @BeforeAll@AfterAll メソッドを使うことができます。

Kotlinプログラミング言語でテストを書いている場合、 ”クラスごと”テストインスタンス・ライフサイクルモードに切り替えることで、 @BeforeAll@AfterAll メソッドの実装がより容易になるかもしれません。

3.9.1. デフォルトのテストインスタンス・ライフサイクルの変更

テストクラスまたはテストインターフェイスに @TestInstance が付与されていない場合、 JUnit Jupiterは デフォルト のライフサイクルモードを使います。 標準的な デフォルト モードは PER_METHOD ですが、 テスト計画全体を実行するための デフォルト を変更することが可能です。 デフォルトのテストインスタンス・ライフサイクルモードを変更するには、 単に junit.jupiter.testinstance.lifecycle.default 設定パラメータTestInstance.Lifecycle に定義されているenum定数名を(大文字・小文字を無視して)設定するだけです。 これは、JVMシステムプロパティとして渡すか、 Launcher に渡される LauncherDiscoveryRequest 内の 設定パラメータ として渡すか、JUnit Platformの設定ファイル (詳細については、 設定パラメータ をご覧ください。)を通して渡します。

例えば、デフォルトのテストインスタンス・ライフサイクルモードを LifeCycle.PER_CLASS に 設定するには、JVMを次のシステムプロパティで起動してください。

-Djunit.jupiter.testinstance.lifecycle.default=per_class

しかしながら、JUnit Platformの設定ファイルを通してデフォルトの テストインスタンス・ライフサイクルモードを設定する方が、より堅牢な解決策です。 設定ファイルはプロジェクトのバージョン管理システムに取り込め、 自身のIDEやビルドソフトウェアで利用できます。

JUnit Platformの設定ファイルを通してデフォルトのテストインスタンス・ライフサイクルモードを 設定ためには、次の内容を含んだ junit-platform.properties という名前のファイルを クラスパス(例えば、 src/test/resources )のルートに生成してください。

junit.jupiter.testinstance.lifecycle.default = per_class

デフォルト のテストインスタンス・ライフサイクルモードを変更することは、 一貫性を持って適用しないと、予測不可能な結果と壊れやすいビルドにつながる恐れがあります。 例えば、ビルドではデフォルトとして”クラスごと”のセマンティックを設定していながら、 IDEでのテストでは"メソッドごと"で実行していた場合、ビルドサーバで起きるエラーを デバッグすることは困難になる恐れがあります。そのため、JVMシステムプロパティの代わりに、 JUnit Platformの設定ファイルを使ってデフォルトを変更することをお薦めします。

3.10. ネストされたテスト

ネストされたテストは、テスト開発者が様々なグループのテスト間の関係を表現することを 可能にします。これがその美しい例です。

スタックをテストするためのネストされたテスト
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.EmptyStackException;
import java.util.Stack;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() {
            assertThrows(EmptyStackException.class, () -> stack.pop());
        }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() {
            assertThrows(EmptyStackException.class, () -> stack.peek());
        }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() {
                assertEquals(anElement, stack.pop());
                assertTrue(stack.isEmpty());
            }

            @Test
            @DisplayName("returns the element when peeked but remains not empty")
            void returnElementWhenPeeked() {
                assertEquals(anElement, stack.peek());
                assertFalse(stack.isEmpty());
            }
        }
    }
}
非静的なネストされたクラス(つまり、内部クラス)のみ@Nested テストクラスとなります。ネストは任意に深くすることができ、 それら内部クラスは一つの例外を除いて、テストクラスの完全なメンバーとして考えられます。 例外は @BeforeAll@AfterAll で、これらは デフォルト では動作しません。 その理由は、Javaが内部クラスに static なメンバーを許さないためです。 しかしながら、この制限は @Nested テストクラスに @TestInstance(Lifecycle.PER_CLASS) を 付与することで回避できます( テストインスタンス・ライフサイクル をご覧ください)。

3.11. コンストラクタとメソッドへの依存性注入

JUnitの前バージョン全てにおいて、テストコンストラクタまたはメソッドは (少なくとも標準的な Runner 実装を用いる場合は)パラメータを持つことが 許されていませんでした。JUnit Jupiterでの大きな変更の1つとして、 テストコンストラクタとメソッドどちらもパラメータを持てるようになりました。 このことは、大きな柔軟性をもたらし、コンストラクタとメソッドに 依存性の注入 が 可能になりました。

ParameterResolver は、実行時に 動的に パラメータを解決することを 望むテスト拡張のためのAPIを定義しています。テストコンストラクタまたは @Test@TestFactory@BeforeEach@AfterEach@BeforeAll@AfterAll メソッドがパラメータを許容する場合は、そのパラメータは登録された ParameterResolver に よって実行時に解決されなければなりません。

現在は、3つの組み込みリゾルバが自動的に登録されます。

  • TestInfoParameterResolver :メソッドパラメータが TestInfo 型の場合、 TestInfoParameterResolver はパラメータの値として現在のテストに応じた TestInfo のインスタンスを供給します。 TestInfo は、テストの表示名、テストクラス、 テストメソッド、関連付けられたタグ名といった現在のテストに関する情報を集めるのに 使うことができます。表示名は、テストクラスまたはテストメソッドの名前といった技術的な名前か、 @DisplayedName で設定されたカスタム名のどちらかです。

TestInfo は、JUnit 4の TestName 規則の代替として動作します。 次のコードは、テストコンストラクタと @BeforeEach メソッド、 @Test メソッドに TestInfo を注入させる方法を示しています。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("TestInfo Demo")
class TestInfoDemo {

    TestInfoDemo(TestInfo testInfo) {
        assertEquals("TestInfo Demo", testInfo.getDisplayName());
    }

    @BeforeEach
    void init(TestInfo testInfo) {
        String displayName = testInfo.getDisplayName();
        assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
    }

    @Test
    @DisplayName("TEST 1")
    @Tag("my-tag")
    void test1(TestInfo testInfo) {
        assertEquals("TEST 1", testInfo.getDisplayName());
        assertTrue(testInfo.getTags().contains("my-tag"));
    }

    @Test
    void test2() {
    }

}
  • RepetitionInfoParameterResolver@RepeatedTest または @BeforeEach@AfterEach メソッドにおけるメソッドパラメータが RepetitionInfo`型の場合、 `RepetitionInfoParameterResolverRepetitionInfo のインスタンスを供給します。 RepetitionInfo は、現在の繰り返しと対応する @RepeatedTest の繰り返しの総数に 関する情報を集めるために利用できます。しかしながら、 RepetitionInfoParameterResolver は、 @RepeatedTest の文脈外では登録されていないことに注意してください。 繰り返しテストの例 をご覧ください。

  • TestReporterParameterResolver:メソッドパラメータが TestReporter 型の場合、 TestReporterParameterResolverTestReporter のインスタンスを供給します。 TestReporter は、現在のテスト実行に関する追加情報を公開するために利用できます。 そのデータは、 TestExecutionListener.reportingEntryPublished() を通して消費され、 IDEに表示またはレポートに含まれます。JUnit Jupiterでは、JUnit 4で stdout`や `stderr に情報を出力していた箇所に TestReporter を使うことができます。 @RunWith(JUnitPlatform.class) を使うと、全てのレポートされたエントリを stdout に出力します。

import java.util.HashMap;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestReporter;

class TestReporterDemo {

    @Test
    void reportSingleValue(TestReporter testReporter) {
        testReporter.publishEntry("a key", "a value");
    }

    @Test
    void reportSeveralValues(TestReporter testReporter) {
        HashMap<String, String> values = new HashMap<>();
        values.put("user name", "dk38");
        values.put("award year", "1974");

        testReporter.publishEntry(values);
    }

}
他のパラメータリゾルバは、 @ExtendWith を用いた適切な 拡張 を 登録することによって明示的に有効化する必要があります。

カスタム ParameterResolver の例に関して RandomParametersExtension を確認しましょう。 リリース可能なものではありませんが、拡張モデルとパラメータ解決プロセス両方の単純さと表現性を 例示しています。 MyRandomParametersTest は、 @Test メソッドへのランダム値の 挿入方法をを示しています。

@ExtendWith(RandomParametersExtension.class)
class MyRandomParametersTest {

    @Test
    void injectsInteger(@Random int i, @Random int j) {
        assertNotEquals(i, j);
    }

    @Test
    void injectsDouble(@Random double d) {
        assertEquals(0.0, d, 1.0);
    }

}

現実的なユースケースとして、 MockitoExtensionSpringExtension のソースコードを確認してください。

3.12. テストインターフェイスとデフォルトメソッド

JUnit Jupiterは、 @Test@RepeatedTest@ParameterizedTest@TestFactory@TestTemplate@BeforeEach@AfterEach に インターフェイスの default メソッドを宣言できるようにしています。 @BeforeAll@AfrterAll はテストインターフェイス内で static メソッドを 宣言するか、 もし テストインターフェイスまたはテストクラスに @TestInstance(Lifecycle.PER_CLASS) が付与されている場合は インターフェイス default メソッドを宣言することができます ( テストインスタンス・ライフサイクル をご覧ください)。 いくつかの例を示します。

@TestInstance(Lifecycle.PER_CLASS)
interface TestLifecycleLogger {

    static final Logger LOG = Logger.getLogger(TestLifecycleLogger.class.getName());

    @BeforeAll
    default void beforeAllTests() {
        LOG.info("Before all tests");
    }

    @AfterAll
    default void afterAllTests() {
        LOG.info("After all tests");
    }

    @BeforeEach
    default void beforeEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("About to execute [%s]",
            testInfo.getDisplayName()));
    }

    @AfterEach
    default void afterEachTest(TestInfo testInfo) {
        LOG.info(() -> String.format("Finished executing [%s]",
            testInfo.getDisplayName()));
    }

}
interface TestInterfaceDynamicTestsDemo {

    @TestFactory
    default Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
            dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
        );
    }

}

@ExtenWith`と `@Tag はテストインターフェイスとして宣言することができるため、 インターフェイスを実装したクラスは自動的にタグと拡張を継承します。 TimingExtension のソースコードを見るには、 BeforeとAfterのテスト実行コールバック をご覧ください。

@Tag("timed")
@ExtendWith(TimingExtension.class)
interface TimeExecutionLogger {
}

テストクラスでは、これらのテストインターフェイスを実装することで適用することができます。

class TestInterfaceDemo implements TestLifecycleLogger,
        TimeExecutionLogger, TestInterfaceDynamicTestsDemo {

    @Test
    void isEqualValue() {
        assertEquals(1, 1, "is always equal");
    }

}

TestInterfaceDemo を実行すると、次と同様の出力が得られます。

:junitPlatformTest
INFO  example.TestLifecycleLogger - Before all tests
INFO  example.TestLifecycleLogger - About to execute [dynamicTestsFromCollection()]
INFO  example.TimingExtension - Method [dynamicTestsFromCollection] took 13 ms.
INFO  example.TestLifecycleLogger - Finished executing [dynamicTestsFromCollection()]
INFO  example.TestLifecycleLogger - About to execute [isEqualValue()]
INFO  example.TimingExtension - Method [isEqualValue] took 1 ms.
INFO  example.TestLifecycleLogger - Finished executing [isEqualValue()]
INFO  example.TestLifecycleLogger - After all tests

Test run finished after 190 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         0 tests skipped         ]
[         3 tests started         ]
[         0 tests aborted         ]
[         3 tests successful      ]
[         0 tests failed          ]

BUILD SUCCESSFUL

この機能の他のあり得る適用としては、インターフェイス契約のためにテストを書くことです。 例えば、 Object.equals または Comparable.compareTo の実装が どう振る舞うべきかのテストを、次のように書くことができます。

public interface Testable<T> {

    T createValue();

}
public interface EqualsContract<T> extends Testable<T> {

    T createNotEqualValue();

    @Test
    default void valueEqualsItself() {
        T value = createValue();
        assertEquals(value, value);
    }

    @Test
    default void valueDoesNotEqualNull() {
        T value = createValue();
        assertFalse(value.equals(null));
    }

    @Test
    default void valueDoesNotEqualDifferentValue() {
        T value = createValue();
        T differentValue = createNotEqualValue();
        assertNotEquals(value, differentValue);
        assertNotEquals(differentValue, value);
    }

}
public interface ComparableContract<T extends Comparable<T>> extends Testable<T> {

    T createSmallerValue();

    @Test
    default void returnsZeroWhenComparedToItself() {
        T value = createValue();
        assertEquals(0, value.compareTo(value));
    }

    @Test
    default void returnsPositiveNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(value.compareTo(smallerValue) > 0);
    }

    @Test
    default void returnsNegativeNumberComparedToSmallerValue() {
        T value = createValue();
        T smallerValue = createSmallerValue();
        assertTrue(smallerValue.compareTo(value) < 0);
    }

}

テストクラスでは、2つの契約インターフェイスを実装することで、対応するテストを継承します。 もちろん、抽象メソッドを実装する必要があります。

class StringTests implements ComparableContract<String>, EqualsContract<String> {

    @Override
    public String createValue() {
        return "foo";
    }

    @Override
    public String createSmallerValue() {
        return "bar"; // 'b' < 'f' in "foo"
    }

    @Override
    public String createNotEqualValue() {
        return "baz";
    }

}
上記のテストは、単なる例であって、完全ではありません。

3.13. 繰り返しテスト

JUnit Jupiterは、 @RepeatedTest を付与し、繰り返してほしい回数を設定するだけで、 特定回数テストを繰り返す機能を提供しています。 繰り返しテストの各呼び出しは、通常の @Test メソッドの実行のように振る舞い、 同じライフサイクル・コールバックと拡張を完全にサポートしています。

次の例は、自動で10回繰り返す repeatedTest() という名前のテストの宣言方法を示しています。

@RepeatedTest(10)
void repeatedTest() {
    // ...
}

繰り返し回数の設定に加えて、 @RepeatedTest アノテーションの name 属性を用いることでカスタム表示名も設定できます。さらに、表示名は、 静的なテキストと動的なプレースホルダの組み合わせで構成されるパターンにすることもできます。 次のプレースホルダが現在サポートされています。

  • {displayName}: @RepeatedTest メソッドの表示名

  • {currentRepetition}: 現在の繰り返し回数

  • {totalRepetition}: 繰り返し回数の合計

ある繰り返し回数時点でのデフォルトの表示名は、次のパターンに基づいて生成されます: 'repetition {currentRepetition} of {totalRepetitions}'。 そのため、先ほどの例の各繰り返し回数における表示名は次のようになります: repetition 1 of 10repetition 2 of 10 など。 @RepeatedTest メソッドの表示名に各繰り返しの名前を含めたい場合は、 独自のカスタムパターンを定義するか、事前定義された RepeatedTest.LONG_DISPLAY_NAME パターンを使うことができます。後者は、 '{displayName} :: repetition {currentRepetition} of {totalRepetitions}' と等しいもので、各繰り返しの表示名は repeatedTest() :: repetition 1 of 10repeatedTest() :: repetition 2 of 10 などとなります。

現在の繰り返し回数と繰り返しの合計数の情報をプログラム的に集めるために、 @RepeatedTest または @BeforeEach@AfterEachRepetitionInfo インスタンスを挿入することができます。

3.13.1. 繰り返しテストの例

この章の最後にある RepeatedTestsDemo クラスは、繰り返しテストの いくつかの例を示しています。

repeatedTest() メソッドは、前章からの例です。 一方、 repeatedTestWithRepetitionInfo() は、現在繰り返されているテストの 繰り返し合計数を得るために RepetitionInfo インスタンスをテストに注入する方法を 示しています。

その次の2つのメソッドは、 @RepeatedTest のカスタム @DisplayName を 各繰り返しの表示名内に含ませる方法を示しています。 customDisplayName() は カスタム表示名とカスタムパターンを組み合わせており、 TestInfo を使って生成された 表示名のフォーマットを検証しています。 Repeat!@DisplayName 宣言から来る {displayName} で、 1/1{currentRepetition}/{totalRepetitions} から来ています。 対照的に、 customDisplayNameWithLongPattern() は、先ほど説明した事前定義の RepeatedTest.LONG_DISPLAY_NAME パターンを使っています。

repeatedTestInGerman() は、繰り返しテストの表示名を他国言語(この場合はドイツ語です)に 翻訳する機能を示しています。その結果、各繰り返しにおける名前は、 Wiederholung 1 von 5Wiederholung 2 von 5 などのようになります。

beforeEach() メソッドは @BeforeEach が付与されているため、 各繰り返しテストの各繰り返し前に実行されます。 TestInfoRepetitionInfo をこのメソッドに注入することで、 現在実行されている繰り返しテストに関する情報を得ることができます。 INFO ログレベルで RepeatedTestsDemo を実行すると出力は次のようになります。

INFO: About to execute repetition 1 of 10 for repeatedTest
INFO: About to execute repetition 2 of 10 for repeatedTest
INFO: About to execute repetition 3 of 10 for repeatedTest
INFO: About to execute repetition 4 of 10 for repeatedTest
INFO: About to execute repetition 5 of 10 for repeatedTest
INFO: About to execute repetition 6 of 10 for repeatedTest
INFO: About to execute repetition 7 of 10 for repeatedTest
INFO: About to execute repetition 8 of 10 for repeatedTest
INFO: About to execute repetition 9 of 10 for repeatedTest
INFO: About to execute repetition 10 of 10 for repeatedTest
INFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 1 of 1 for customDisplayName
INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern
INFO: About to execute repetition 1 of 5 for repeatedTestInGerman
INFO: About to execute repetition 2 of 5 for repeatedTestInGerman
INFO: About to execute repetition 3 of 5 for repeatedTestInGerman
INFO: About to execute repetition 4 of 5 for repeatedTestInGerman
INFO: About to execute repetition 5 of 5 for repeatedTestInGerman
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.logging.Logger;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.TestInfo;

class RepeatedTestsDemo {

    private Logger logger = // ...

    @BeforeEach
    void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {
        int currentRepetition = repetitionInfo.getCurrentRepetition();
        int totalRepetitions = repetitionInfo.getTotalRepetitions();
        String methodName = testInfo.getTestMethod().get().getName();
        logger.info(String.format("About to execute repetition %d of %d for %s", //
            currentRepetition, totalRepetitions, methodName));
    }

    @RepeatedTest(10)
    void repeatedTest() {
        // ...
    }

    @RepeatedTest(5)
    void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {
        assertEquals(5, repetitionInfo.getTotalRepetitions());
    }

    @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")
    @DisplayName("Repeat!")
    void customDisplayName(TestInfo testInfo) {
        assertEquals(testInfo.getDisplayName(), "Repeat! 1/1");
    }

    @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)
    @DisplayName("Details...")
    void customDisplayNameWithLongPattern(TestInfo testInfo) {
        assertEquals(testInfo.getDisplayName(), "Details... :: repetition 1 of 1");
    }

    @RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}")
    void repeatedTestInGerman() {
        // ...
    }

}

ConsoleLauncher またはunicodeテーマを有効化した junitPlatformTest Gradleプラグインを 使うと、 RepeatedTestsDemo の実行結果は次のようなコンソール出力を行います。

├─ RepeatedTestsDemo ✔
│  ├─ repeatedTest() ✔
│  │  ├─ repetition 1 of 10 ✔
│  │  ├─ repetition 2 of 10 ✔
│  │  ├─ repetition 3 of 10 ✔
│  │  ├─ repetition 4 of 10 ✔
│  │  ├─ repetition 5 of 10 ✔
│  │  ├─ repetition 6 of 10 ✔
│  │  ├─ repetition 7 of 10 ✔
│  │  ├─ repetition 8 of 10 ✔
│  │  ├─ repetition 9 of 10 ✔
│  │  └─ repetition 10 of 10 ✔
│  ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔
│  │  ├─ repetition 1 of 5 ✔
│  │  ├─ repetition 2 of 5 ✔
│  │  ├─ repetition 3 of 5 ✔
│  │  ├─ repetition 4 of 5 ✔
│  │  └─ repetition 5 of 5 ✔
│  ├─ Repeat! ✔
│  │  └─ Repeat! 1/1 ✔
│  ├─ Details... ✔
│  │  └─ Details... :: repetition 1 of 1 ✔
│  └─ repeatedTestInGerman() ✔
│     ├─ Wiederholung 1 von 5 ✔
│     ├─ Wiederholung 2 von 5 ✔
│     ├─ Wiederholung 3 von 5 ✔
│     ├─ Wiederholung 4 von 5 ✔
│     └─ Wiederholung 5 von 5 ✔

3.14. パラメータ化テスト

パラメータ化テストを使うと、テストを異なる引数で複数回実行できるようになります。 パラメータ化テストは、通常の @Test メソッドの代わりに @ParameterizedTest アノテーションを付与するだけで宣言することができます。 さらに、各呼び出して供給されテストで 消費される 引数として、 少なくとも1つの source を宣言する必要があります。

次の例は、パラメータ化テストを示していて、 @ValueSource アノテーションを使って 引数のソースとして String 配列を指定しています。

@ParameterizedTest
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" })
void palindromes(String candidate) {
    assertTrue(isPalindrome(candidate));
}

上記のパラメータ化テストメソッドを実行すると、各呼び出しは別々にレポートされます。 例えば、 ConsoleLauncher は次のようなものを出力します。

palindromes(String) ✔
├─ [1] racecar ✔
├─ [2] radar ✔
└─ [3] able was I ere I saw elba ✔
パラメータ化テストは、現在 実験的な 機能です。 詳細については、 実験的な APIs をご覧ください。

3.14.1. 必要なセットアップ

パラメータ化テストを使うためには、 junit-jupiter-params アーティファクトを 依存関係に加える必要があります。詳細については、 依存関係のメタデータ をご覧ください。

3.15. 引数の消費

パラメータ化テストメソッドは典型的に、設定されたソース ( 引数のソース をご覧ください。) から直接、引数を 消費 します。引数ソースとメソッドパラメータのインデックスは 1対1の相関関係に従います( @CsvSource の例をご覧ください)。 しかしながら、パラメータ化テストメソッドは、ソースから得た引数をひとつのオブジェクトに 集約 して、メソッドに渡すこともできます( 引数集約 をご覧ください)。 追加的な引数もまた(例えば、 TestInfoTestReporter などのインスタンスを 獲得するために) ParameterResolver によって提供されます。 特に、パラメータ化テストメソッドは、次のルールに従って形式的なパラメータを宣言する必要があります。

  • まず、0個以上の インデックスされた引数 を宣言する。

  • 次に、0個以上の アグリゲータ を宣言する。

  • 最後に、0個以上の ParameterResolver によって供給される引数を宣言する。

この文脈で、 インデックスされた引数 とは、 ArgumentsProvider によって提供される Arguments 内で与えられたインデックスに対応する引数です。 ArgumentsProvider は、パラメータ化メソッドが保持する形式的なパラメータリストにおいて 同じインデックスにあるメソッドに引数として渡されます。 アグリゲータ は、 ArgumentsAccessor 型または @AggregateWith の付与されたパラメータです。

3.15.1. 引数のソース

すぐに使えるように、JUnit Jupiterは非常に多くの ソース アノテーションを提供しています。 次の各章はそれぞれ、簡潔な概要とそれぞれの例を提供しています。 さらなる情報に関しては、 org.junit.jupiter.params.provider パッケージのJavaDocを参照してください。

@ValueSource

@ValueSource は最も単純なソースの1つです。 リテラル値の配列を1つ設定することができ、パラメータ化テスト呼び出しにつき、 1つのパラメータを提供できます。

次のリテラル値の型が @ValueSource にサポートされています。

  • short

  • byte

  • int

  • long

  • float

  • double

  • char

  • java.lang.String

  • java.lang.Class

例えば、次の @ParameterizedTest メソッドはそれぞれ 123 の値とともに 3回呼び出されます。

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertTrue(argument > 0 && argument < 4);
}
@EnumSource

@EnumSource は、 Enum 定数に対して便利な機能を提供します。 このアノテーションは、使われる定数を特定するために、オプションで names パラメータを 提供します。省略する場合は、次の例のように全ての定数が使われます。

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnumSource(TimeUnit timeUnit) {
    assertNotNull(timeUnit);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = { "DAYS", "HOURS" })
void testWithEnumSourceInclude(TimeUnit timeUnit) {
    assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}

@EnumSource アノテーションはまた、テストメソッドに渡すパラメータを細かく制御するために、 オプションで mode パラメータを提供します。例えば、次の例では、enum定数プールから namesを取り除いたり、正規表現を設定しています。

@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EXCLUDE, names = { "DAYS", "HOURS" })
void testWithEnumSourceExclude(TimeUnit timeUnit) {
    assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
    assertTrue(timeUnit.name().length() > 5);
}
@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = MATCH_ALL, names = "^(M|N).+SECONDS$")
void testWithEnumSourceRegex(TimeUnit timeUnit) {
    String name = timeUnit.name();
    assertTrue(name.startsWith("M") || name.startsWith("N"));
    assertTrue(name.endsWith("SECONDS"));
}
@MethodSource

@MethodSource では、テストクラスまたは外部クラスの ファクトリー メソッドを 1つ以上使うことができます。ファクトリーメソッドは、 Stream または IterableIterator 、引数の配列を返す必要があります。さらに、ファクトリーメソッドには 引数を与えられません。テストクラス内のファクトリーメソッドは、テストクラスに @TestInstance(Lifecycle.PER_CLASS) が付与されていない限り、 static である必要があります。一方、外部クラスのファクトリーメソッドは 常に static である必要があります。

パラメータが1つだけ必要な場合は、次の例が示しているように、パラメータの型のインスタンスの`Stream`を返すことができます。

@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
    assertNotNull(argument);
}

static Stream<String> stringProvider() {
    return Stream.of("foo", "bar");
}

@MethodSource を通して明示的にファクトリーメソッドの名前を提供しない場合、 JUnit Jupiterは、慣例にならって現在の @ParameterizedTest と 同じ名前を持つ ファクトリー メソッドを探します。これを次の例で示します。

@ParameterizedTest
@MethodSource
void testWithSimpleMethodSourceHavingNoValue(String argument) {
    assertNotNull(argument);
}

static Stream<String> testWithSimpleMethodSourceHavingNoValue() {
    return Stream.of("foo", "bar");
}

DoubleStreamIntStreamLongStream といったプリミティブ型の Streamもまた、次の例のようにサポートされています。

@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(int argument) {
    assertNotEquals(9, argument);
}

static IntStream range() {
    return IntStream.range(0, 20).skip(10);
}

テストメソッドが複数のパラメータを宣言している場合、 下に示すように Arguments インスタンスのコレクションまたはストリームを 返す必要があります。 Arguments.of(Object…​) は、 Arguments インターフェイスで定義されている静的なファクトリーメソッドです。

@ParameterizedTest
@MethodSource("stringIntAndListProvider")
void testWithMultiArgMethodSource(String str, int num, List<String> list) {
    assertEquals(3, str.length());
    assertTrue(num >=1 && num <=2);
    assertEquals(2, list.size());
}

static Stream<Arguments> stringIntAndListProvider() {
    return Stream.of(
        Arguments.of("foo", 1, Arrays.asList("a", "b")),
        Arguments.of("bar", 2, Arrays.asList("x", "y"))
    );
}

外部の static ファクトリー メソッドは、 次の例で示すように 完全修飾メソッド名 によって参照されます。

package example;

import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

class ExternalMethodSourceDemo {

    @ParameterizedTest
    @MethodSource("example.StringsProviders#blankStrings")
    void testWithExternalMethodSource(String blankString) {
        // test with blank string
    }
}

class StringsProviders {

    static Stream<String> blankStrings() {
        return Stream.of("", " ", " \n ");
    }
}
@CsvSource

@CsvSource は、引数リストをコンマ区切りの値(つまり、 String リテラル)として 表現できるようにします。

@ParameterizedTest
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCsvSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}

@CsvSource は、シングルクォーテーション ' を引用文字として使います。 上の例と下の表の 'baz, qux' の値をご覧ください。 引用された空の値 '' は、空の String となります。 一方、完全に の値は null 参照として解釈されます。 null 参照が対象とする型がプリミティブ型の場合、 ArgumentConversionException が投げられます。

入力例 引数リストの結果

@CsvSource({ "foo, bar" })

"foo", "bar"

@CsvSource({ "foo, 'baz, qux'" })

"foo" , "baz, qux"

@CsvSource({ "foo, ''" })

"foo", ""

@CsvSource({ "foo, " })

"foo", null

@CsvFileSource

@CsvFileSource は、CSVファイルをクラスパスから使えるようにします。 パラメータ化テストが1回呼び出される度に、CSVファイルの各行が読み込まれます。

@ParameterizedTest
@CsvFileSource(resources = "two-column.csv", numLinesToSkip = 1)
void testWithCsvFileSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}
two-column.csv
Country, reference
Sweden, 1
Poland, 2
"United States of America", 3
@CsvSource で使われている構文とは対照的に、 @CsvFileSource では引用文字としてダブルクォーテーション " を使います。 上記の例の "United States of America" をご覧ください。 引用された空の値 ”” は、空の String となります。 一方、完全に の値は null 参照として解釈されます。 null 参照が対象とする型がプリミティブ型の場合、 ArgumentConversionException が 投げられます。
@ArgumentSource

@ArgumentSource はカスタムの再利用可能な ArgumentsProvider を 特定するために使うことができます。

@ParameterizedTest
@ArgumentsSource(MyArgumentsProvider.class)
void testWithArgumentsSource(String argument) {
    assertNotNull(argument);
}

public class MyArgumentsProvider implements ArgumentsProvider {

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return Stream.of("foo", "bar").map(Arguments::of);
    }
}

3.15.2. 引数変換

拡大的な変換

JUnit Jupiterは @ParamterizedTest に供給する引数のために、 拡大的なプリミティブ変換を サポートしています。例えば、 @ValueSource(ints = { 1, 2, 3 }) が付与された パラメータ化テストは、 int 型のみならず、 longfloatdouble 型の 引数も受けることができます。

暗示的な変換

@CsvSource のようなユースケースをサポートするために、 JUnit Jupiterは組み込みの暗示的な型変換をいくつか提供しています。 変換プロセスは、各メソッドパラメータの宣言された型に依存します。

例えば、 @ParameterizedTestTimeUnit 型のパラメータを宣言していて、 ソースから供給された実際の型が String であった場合、 String は 自動的に対応する TimeUnit enum定数に変換されます。

@ParameterizedTest
@ValueSource(strings = "SECONDS")
void testWithImplicitArgumentConversion(TimeUnit argument) {
    assertNotNull(argument.name());
}

String インスタンスは現在、次の対象型に暗示的に変換されます。

対象型

boolean/Boolean

"true"true

byte/Byte

"1" → (byte) 1

char/Character

"o"'o'

short/Short

"1" → (short) 1

int/Integer

"1"1

long/Long

"1"1L

float/Float

"1"1.0f

double/Double

"1"1.0d

`Enum`サブクラス

"SECONDS"TimeUnit.SECONDS

java.io.File

"/path/to/file"new File("path/to/file")

java.math.BigDecimal

"123.456e789"new BigDecimal("123.456e789")

java.math.BigInteger

"1234567890123456789"new BigInteger("1234567890123456789")

java.net.URI

"http://junit.org/";URI.create("http://junit.org/")

java.net.URL

"http://junit.org/";new URL("http://junit.org/")

java.nio.file.Path

"/path/to/file"Paths.get("/path/to/file")

java.time.Instant

"1970-01-01T00:00:00Z"Instant.ofEpochMilli(0)

java.time.LocalDateTime

"2017-03-14T12:34:56.789"LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)

java.time.LocalDate

"2017-03-14"LocalDate.of(2017, 3, 14)

java.time.LocalTime

"12:34:56.789"LocalTime.of(12, 34, 56, 789_000_000)

java.time.OffsetDateTime

"2017-03-14T12:34:56.789Z"OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.OffsetTime

"12:34:56.789Z"OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.YearMonth

"2017-03"YearMonth.of(2017, 3)

java.time.Year

"2017"Year.of(2017)

java.time.ZonedDateTime

"2017-03-14T12:34:56.789Z"ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.util.Currency

"JPY"Currency.getInstance("JPY")

java.util.Locale

"en"new Locale("en")

java.util.UUID

"d043e930-7b3b-48e3-bdbe-5a3ccfb833db"UUID.fromString("d043e930-7b3b-48e3-bdbe-5a3ccfb833db")

StringからObjectへの予備的な変換

Stringから上に列挙されている対象型への暗示的な変換に加えて、 JUnit Jupiterでは、対象型が下の定義に合致した ファクトリーメソッド または ファクトリーコンストラクタ を宣言する場合、 `String`をその対象型へ 自動変換する予備的な機構を提供します。

  • ファクトリーメソッド :対象型で宣言されている非プライベートかつ static なメソッドで、 1つの String 引数を取り、対象型のインスタンスを返すもの。 メソッド名は任意であり、特定の慣習にも従う必要はありません。

  • ファクトリーコンストラクタ :対象型のプライベートでないコンストラクタで、 1つの `String`引数を取るもの。

複数の ファクトリーメソッド が見つかった場合、それらは無視されます。 ファクトリーメソッドファクトリーコンストラクタ が見つかった場合、 ファクトリーメソッドがコンストラクタの代わりに使われます。

例えば、次の @ParameterizedTest メソッドの中で、 Book 引数は Book.fromTitle(String) ファクトリーメソッドが呼び出されることで生成され、 "42 Cats" が本のタイトルとして渡されます。

@ParameterizedTest
@ValueSource(strings = "42 Cats")
void testWithImplicitFallbackArgumentConversion(Book book) {
    assertEquals("42 Cats", book.getTitle());
}

public class Book {

    private final String title;

    private Book(String title) {
        this.title = title;
    }

    public static Book fromTitle(String title) {
        return new Book(title);
    }

    public String getTitle() {
        return this.title;
    }
}
明示的な変換

暗黙的な引数変換の代わりに、次の例のように @ConvertWith アノテーションを使うことで、 あるパラメータに対して明示的に ArgumentConverter を特定することができます。

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithExplicitArgumentConversion(
        @ConvertWith(ToStringArgumentConverter.class) String argument) {

    assertNotNull(TimeUnit.valueOf(argument));
}

public class ToStringArgumentConverter extends SimpleArgumentConverter {

    @Override
    protected Object convert(Object source, Class<?> targetType) {
        assertEquals(String.class, targetType, "Can only convert to String");
        return String.valueOf(source);
    }
}

明示的な引数変換は、テストと拡張の開発者によって実装される必要があります。 そのため、 junit-jupiter-params では、参照実装として使える明示的な引数変換器: JavaTimeArgumentConverter を提供しています。 合成アノテーションである JavaTimeConversionPattern を通して使うことができます。

@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(
        @JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {

    assertEquals(2017, argument.getYear());
}

3.15.3. 引数集約

デフォルトでは、 @ParameterizedTest メソッドに渡される各 引数 は、 1つのメソッドパラメータに対応しています。その結果として、大量の引数を供給することが 期待される引数ソースは、巨大なメソッドシグネチャになる可能性があります。

そのような場合、 ArgumentsAccessor を複数のパラメータの代わりに使うことができます。 このAPIを使うことで、テストメソッドに渡された1つのパラメータを通して提供された 引数にアクセスすることができます。さらに、 暗黙的な変換 で議論している型変換もサポートしています。

@ParameterizedTest
@CsvSource({
    "Jane, Doe, F, 1990-05-20",
    "John, Doe, M, 1990-10-22"
})
void testWithArgumentsAccessor(ArgumentsAccessor arguments) {
    Person person = new Person(arguments.getString(0),
                               arguments.getString(1),
                               arguments.get(2, Gender.class),
                               arguments.get(3, LocalDate.class));

    if (person.getFirstName().equals("Jane")) {
        assertEquals(Gender.F, person.getGender());
    }
    else {
        assertEquals(Gender.M, person.getGender());
    }
    assertEquals("Doe", person.getLastName());
    assertEquals(1990, person.getDateOfBirth().getYear());
}

ArgumentsAccessor のインスタンス は、 ArgumentsAccessor 型のいかなるパラメータにも自動的に挿入されます。

カスタムアグリゲータ

ArgumentsAccessor を用いた @ParameterizedTest メソッドの引数への 直接アクセスとは別に、JUnit Jupiterはカスタムで再利用可能な アグリゲータ の使用も サポートしています。

カスタムアグリゲータを使うためには、単に ArgumentsAggregator`インターフェイスを実装し、 `@ParameterizedTest メソッド内で互換可能なパラメータに対して @AggregateWith アノテーションを付与して登録するだけです。 集約の結果は、パラメータ化テストが呼び出された時に、対応するパラメータへの引数として 提供されます。

@ParameterizedTest
@CsvSource({
    "Jane, Doe, F, 1990-05-20",
    "John, Doe, M, 1990-10-22"
})
void testWithArgumentsAggregator(@AggregateWith(PersonAggregator.class) Person person) {
    // perform assertions against person
}

public class PersonAggregator implements ArgumentsAggregator {
    @Override
    public Person aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) {
        return new Person(arguments.getString(0),
                          arguments.getString(1),
                          arguments.get(2, Gender.class),
                          arguments.get(3, LocalDate.class));
    }
}

コードベースにまたがって複数のパラメータ化テストに対して繰り返し @AggregateWith(MyTypeAggregator.class) を宣言している場合、 @AggregateWith(MyTypeAggregator.class) のメタアノテーションとして @CsvToMyType のようなカスタム 合成アノテーション を作成できます。 次の例は、カスタム @CsvToPerson アノテーションを用いた動作例を示しています。

@ParameterizedTest
@CsvSource({
    "Jane, Doe, F, 1990-05-20",
    "John, Doe, M, 1990-10-22"
})
void testWithCustomAggregatorAnnotation(@CsvToPerson Person person) {
    // perform assertions against person
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@AggregateWith(PersonAggregator.class)
public @interface CsvToPerson {
}

3.15.4. 表示名のカスタマイズ

デフォルトでは、パラメータ化テスト呼び出しの表示名は、 呼び出しインデックスと特定の呼び出しに対する全ての引数の String 表現を含んでいます。 しかしながら、次の例のように @ParameterizedTest アノテーションの name 属性によって呼び出し表示名をカスタマイズできます。

@DisplayName("Display name of container")
@ParameterizedTest(name = "{index} ==> first=''{0}'', second={1}")
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCustomDisplayNames(String first, int second) {
}

上記のメソッドを ConsoleLauncher を使って実行すると、次のような出力が表示されます。

Display name of container ✔
├─ 1 ==> first='foo', second=1 ✔
├─ 2 ==> first='bar', second=2 ✔
└─ 3 ==> first='baz, qux', second=3 ✔

カスタム表示名では、次のプレースホルダがサポートされています。

プレースホルダ 説明

{index}

現在の呼び出しインデックス(1始まり)

{arguments}

完全な引数リスト(CSV形式)

{0}, {1}, …

各引数

3.15.5. ライフサイクルと相互運用性

パラメータ化テストの各呼び出しは、通常の @Test メソッドと 同じライフサイクルを持っています。例えば、各呼び出し前には @BeforeEach メソッドが実行されます。 動的テスト と同じように、 呼び出しはIDEのテストツリーでは一つ一つ表れます。同一のテストクラスに、 自由に @Test@ParameterizedTest を混ぜることができます。

@ParameterizedTest メソッドと合わせて ParameterResolver 拡張を使うことができます。 しかしながら、引数ソースによって解決されたパラメータは、 引数リストの最初に来る必要があります。 テストクラスは様々なパラメータリストを持つパラメータ化テストと同様に 通常のテストを含むこともあるので、引数ソースからの値は @BeforeEach といった ライフサイクルメソッドやテストクラスコンストラクタは解決されません。

@BeforeEach
void beforeEach(TestInfo testInfo) {
    // ...
}

@ParameterizedTest
@ValueSource(strings = "foo")
void testWithRegularParameterResolver(String argument, TestReporter testReporter) {
    testReporter.publishEntry("argument", argument);
}

@AfterEach
void afterEach(TestInfo testInfo) {
    // ...
}

3.16. テストテンプレート

@TestTemplate メソッドは、通常のテストケースではなく、 むしろテストケースのためのテンプレートです。 したがって、 @TestTemplate は、登録されたプロバイダによって返される 呼び出し文脈の数に応じて複数回呼び出されるものとして設計されています。 そのため、登録された TestTemplateInvocationContextProvider 拡張と併せて使われる必要があります。 テストテンプレートメソッドの各呼び出しは、通常の @Test メソッドの実行と 同じように振る舞い、同じライフサイクルのコールバックと拡張が完全にサポートされています。 用法例については、 テストテンプレートに対する呼び出し文脈の提供 を参照ください。

3.17. 動的テスト

アノテーション で説明したJUnit Jupiterの 標準的な @Test アノテーションは、JUnit 4の @Test アノテーションに非常に似通っています。 どちらもテストケースを実装したメソッドです。これらのテストケースはコンパイル時に 完全に決定するという意味では静的であり、それらの振る舞いは実行時に変更することはできません。 アサンプションは、意図的にかなり表現性に制限のあるものですが、動的な振る舞いの基本的な形式を提供します

これらの標準的なテストに加えて、全く新しい種類のテストプログラミングモデルが JUnit Jupiterでは導入されました。 この新しいテストとは、 動的テスト です。 動的テストは、 @TestFactory が付与されたファクトリーメソッドによって、 実行時に生成されます。

@Test メソッドとは対照的に、 @TestFactory メソッド自身はテストケースではなく、 むしろテストケースのためのファクトリーです。 そのため、動的テストはファクトリーの産出物となります。 技術的なことを言うと、 @TestFactory メソッドは、 DynamicNode インスタンスの Stream または CollectionIterableIterator を返さなければなりません。 DynamicNode のインスタンス化可能なサブクラスは DynamicContainerDynamicTest です。 DynamicContainer インスタンスは、 表示名 と動的な子ノードのリストで構成されており、 動的なノードの任意なネスト階層を生成できます。 DynamicTest インスタンスは、遅延実行され、テストケースの動的で非決定的な生成が 可能となります。

@TestFactory によって返される Stream はいずれも、 stream.close() を 呼ぶことで適切に閉じられます。これによって、 Files.lines() のような資源を 安全に使うことができます。

@Test メソッドと同様に、 @TestFactory メソッドは private または static は不可で、 ParameterResolvers で解決されるパラメータをオプションで宣言できます。

DynamicTest は実行時に生成されるテストケースで、 表示名Executable で 構成されています。 Executable@FunctionalInterface で、 このインターフェイスは ラムダ表現 または メソッド参照 として提供されることができる 動的テストの実装であることを意味します。

動的テストのライフサイクル
動的テストの実行ライフサイクルは、標準的な @Test ケースとは全く異なります。 特に、各動的テストに対してのライフサイクルのコールバックはありません。 このことは、 @BeforeEach@AfterEach 、それに対応した拡張コールバックは @TestFactory メソッドに対して実行され、各 動的テスト には実行されないことを意味します。 つまり、動的テストに対してラムダ表現でテストインスタンスからフィールドにアクセスしても、 それらのフィールドは、同じ @TestFactory メソッドで生成された 個々の動的テストの実行中は、コールバックメソッドやその拡張によってリセットされません。

JUnit Jupiter 5.2.0 時点では、 動的テストは常にファクトリーメソッドによって生成される必要があります。 しかしながら、これは後のリリースにある登録機能によって補完されるかもしれません。

動的テストは現在 実験的な 機能です。 詳細に関しては、 実験的な APIs をご覧ください。

3.17.1. 動的テストの例

次の DynamicTestsDemo クラスは、 テストファクトリーと動的テストのいくつかの例を示しています。

最初のメソッドは不正な型を返しています。 不正な返り値の型はコンパイル時に検出することができないため、 実行時に検出され JUnitException が投げられます。

次の5つのメソッドは、 DynamicTest インスタンスの Collection または IterableIteratorStream を生成する非常に単純な例です。 これらの例のほとんどは実際には動的振る舞いを示しておらず、 単に原則的にサポートされている返却型を示しています。 しかしながら、 dynamicTestsFromStream()dynamicTestsFromIntStream() は、 String のセットや入力値の範囲に対する動的テストの生成がいかに簡単かを示しています。

次のメソッドは、性質上、真に動的なものです。 generateRandomNumberOfTests() 、ランダム数を生成する Iterator と表示名生成器、 テスト実行器を実装しており、その3つを DynamicTest.stream() に提供しています。 generateRandomNumberOfTests() の非決定的な振る舞いは、 もちろんテスト反復可能性に抵触しており、注意深く取り扱われるべきではありますが、 動的テストの表現性と能力を示しています。

最後のメソッドは、 DynamicContainer を使って動的テストのネスト階層を生成しています。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;

class DynamicTestsDemo {

    // This will result in a JUnitException!
    @TestFactory
    List<String> dynamicTestsWithInvalidReturnType() {
        return Arrays.asList("Hello");
    }

    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
            dynamicTest("1st dynamic test", () -> assertTrue(true)),
            dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
        );
    }

    @TestFactory
    Iterable<DynamicTest> dynamicTestsFromIterable() {
        return Arrays.asList(
            dynamicTest("3rd dynamic test", () -> assertTrue(true)),
            dynamicTest("4th dynamic test", () -> assertEquals(4, 2 * 2))
        );
    }

    @TestFactory
    Iterator<DynamicTest> dynamicTestsFromIterator() {
        return Arrays.asList(
            dynamicTest("5th dynamic test", () -> assertTrue(true)),
            dynamicTest("6th dynamic test", () -> assertEquals(4, 2 * 2))
        ).iterator();
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromStream() {
        return Stream.of("A", "B", "C")
            .map(str -> dynamicTest("test" + str, () -> { /* ... */ }));
    }

    @TestFactory
    Stream<DynamicTest> dynamicTestsFromIntStream() {
        // Generates tests for the first 10 even integers.
        return IntStream.iterate(0, n -> n + 2).limit(10)
            .mapToObj(n -> dynamicTest("test" + n, () -> assertTrue(n % 2 == 0)));
    }

    @TestFactory
    Stream<DynamicTest> generateRandomNumberOfTests() {

        // Generates random positive integers between 0 and 100 until
        // a number evenly divisible by 7 is encountered.
        Iterator<Integer> inputGenerator = new Iterator<Integer>() {

            Random random = new Random();
            int current;

            @Override
            public boolean hasNext() {
                current = random.nextInt(100);
                return current % 7 != 0;
            }

            @Override
            public Integer next() {
                return current;
            }
        };

        // Generates display names like: input:5, input:37, input:85, etc.
        Function<Integer, String> displayNameGenerator = (input) -> "input:" + input;

        // Executes tests based on the current input value.
        ThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);

        // Returns a stream of dynamic tests.
        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);
    }

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        return Stream.of("A", "B", "C")
            .map(input -> dynamicContainer("Container " + input, Stream.of(
                dynamicTest("not null", () -> assertNotNull(input)),
                dynamicContainer("properties", Stream.of(
                    dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                    dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
                ))
            )));
    }

}

4. テストの実行

4.1. IDEサポート

4.1.1. IntelliJ IDEA

IntelliJ IDEAは、バージョン 2016.2から、JUnit Platform上でのテスト実行をサポートしています。 詳細については、 IntelliJ IDEAブログの記事 をご覧ください。 しかしながら、2017.3以降のバージョンのIDEAを使うことをお薦めします。 というのも、これらの新しいバージョンのIDEAは、プロジェクト内で使われているAPIバージョンに応じて 次のJARを自動でダウンロードしてくれるからです: junit-platform-launcherjunit-jupiter-enginejunit-vintage-engine

IDEA 2017.3より前のIntelliJ IDEAは、 特定のバージョンのJUnit 5をバンドルします。 そのため、新しいバージョンのJUnit Jupiterを使いたい場合、 IDE内でのテスト実行はバージョンの衝突によって失敗する恐れがあります。 そのような場合、ItelliJ IDEAにバンドルされたものではない新しいJUnit 5を 使うために下の説明に従ってください。

異なるJUnit 5のバージョン(例えば、 5.2.0 )を使うには、 対応するバージョンの junit-platform-launcherjunit-jupiter-enginejunit-vintage-engine のJARをクラスパスに含める必要があるかもしれません。

追加のGradleの依存関係
// Only needed to run tests in a version of IntelliJ IDEA that bundles older versions
testRuntime("org.junit.platform:junit-platform-launcher:1.2.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.2.0")
testRuntime("org.junit.vintage:junit-vintage-engine:5.2.0")
追加のMavenの依存関係
<!-- Only needed to run tests in a version of IntelliJ IDEA that bundles older versions -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.2.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>

4.1.2. Eclipse

Eclipse IDEは、Eclipse Oxygen.1a (4.7.1a)から JUnit Platformサポートを提供しています。

EclipseでJUnit 5を使うためのさらなる情報については、 Eclipse Project Oxygen.1a (4.7.1a) - New and Noteworthy 記事にある公式 Eclipse support for JUnit 5 の章をご覧ください。

4.1.3. 他のIDE

この記事を書いている時点では、IDEAとEclipseを除き、IDE内でJUnit Platform上のテスト実行を 直接サポートしているIDEはありません。しかしながら、いまいまでも自身のIDEでJUnit 5を 試すことができるように、JUnitチームは2つの仲介的な解決法を提供しています。 それは コンソール・ラウンチャー を手動で使うか、 JUnit 4ベースのランナー でテストを実行することができます。

4.2. ビルドサポート

4.2.1. Gradle

バージョン 4.6 から、 GradleはJUnit Platform上でのテスト実行を ネイティブ・サポート しています。 それを有効化するためには、 build.gradle 内の test タスク宣言で useJUnitPlatform() を指定するだけです。

test {
    useJUnitPlatform()
}

タグやエンジンによるフィルタリングもサポートされています:

test {
    useJUnitPlatform {
        includeTags 'fast', 'smoke & feature-a'
        // excludeTags 'slow', 'ci'
        includeEngines 'junit-jupiter'
        // excludeEngines 'junit-vintage'
    }
}

オプションの包括的なリストについては、 Gradleの公式ドキュメント を参照してください。

JUnit Platform Gradleプラグインは非推奨

JUnitチームによって開発された非常に初歩的な junit-platform-gradle-plugin は、 JUnit Platform 1.2で非推奨になり、1.3では廃止されます。 Gradleの標準的な test タスクに移行してください。

設定パラメータ

標準的なGralde test タスクは現在、テスト発見や実行に役立つJUnit Platform 設定パラメータ を設定するための専用DSLを提供していません。 しかしながら、(下に示すように)システムプロパティや junit-platform.properties を通して、ビルドスクリプト内で設定パラメータを提供できます。

test {
    // ...
    systemProperty 'junit.jupiter.conditions.deactivate', '*'
    systemProperties = [
        'junit.jupiter.extensions.autodetection.enabled': 'true',
        'junit.jupiter.testinstance.lifecycle.default': 'per_class'
    ]
    // ...
}
テストエンジンの設定

テストを実行するには、 TestEngine 実装をクラスパス上に配置する必要があります。

JUnit Jupiterベースのテストへのサポートを設定するには、 次のように、 testCompile の依存関係にJUnit Jupiter APIと、 testRuntime の依存関係にJUnit Jupiter TestEngine 実装を設定する必要があります。

dependencies {
    testCompile("org.junit.jupiter:junit-jupiter-api:5.2.0")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.2.0")
}

JUnit Platformは、次のように、 testCompile の依存関係にJUnit 4と、 testRuntime の依存関係にJUnit Vintage TestEngine 実装を設定することで、 JUnit 4ベースのテストを実行することができます。

dependencies {
    testCompile("junit:junit:4.12")
    testRuntime("org.junit.vintage:junit-vintage-engine:5.2.0")
}
ログの設定(オプション)

JUnitは、 JUL で知られている java.util.logging パッケージ内の Java Logging APIsを使って、警告とデバッグ情報を排出しています。 設定オプションについては、 LogManager の公式ドキュメントを参照してください。

その他に、ログメッセージを Log4JLogback といった 他のロギングフレームワークにログメッセージをリダイレクトすることもできます。 LogManager の カスタム実装を提供しているロギングフレームワークを使うには、 java.util.logging.manager システムプロパティに、使う LogManager 実装の 完全修飾クラス名 を設定してください。 下の例では、Log4j 2.xの設定方法を示しています (詳細については、 Log4J JDK ロギングアダプタ をご覧ください)。

test {
    systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
}

他のロギングフレームワークは、 java.util.logging を使ってログされた メッセージをリダイレクトするための他の方法を提供しています。 例えば、 Logback では、 実行時のクラスパスに依存関係を追加することで、 JULからSLF4Jへの橋渡しを使うことができます。

4.2.2. Maven

JUnitチームは、 mvn test を通してJUnit 4とJUnit Jupiterのテストを実行できるような Maven Surefireのための基本的なプロバイダを開発しました。 junit5-jupiter-starter-mavenプロジェクト内の pom.xml ファイルは、開始ポイントとしてのプロバイダの利用方法を示しています。

unit-platform-surefire-provider を用いて Maven Surefire 2.21.0 を使ってください。
...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>{surefire-version}</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>{platform-version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
テストエンジンの設定

Maven Surefireでテストを実行するには、 TestEngine 実装を実行時クラスパスに加える必要があります。

JUnit Jupiterベースのテストへのサポートを設定するには、 次のように、JUnit Jupiter API上に test の依存関係を設定し、 maven-surefire-plugin の依存関係にJUnit Jupiter TestEngine 実装を追加してください。

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>{surefire-version}</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>{platform-version}</version>
                </dependency>
                <dependency>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-engine</artifactId>
                    <version>{jupiter-version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
<dependencies>
    ...
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>{jupiter-version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>
...

JUnit Platform Surefire Providerは、 次のように、JUnit 4上に`test`の依存関係を設定し、 maven-surefire-plugin の依存関係に JUnit Vintage `TestEngine`実装を追加することで、JUnit 4ベースのテストを実行できます。

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.21.0</version>
            <dependencies>
                <dependency>
                    <groupId>org.junit.platform</groupId>
                    <artifactId>junit-platform-surefire-provider</artifactId>
                    <version>{platform-version}</version>
                </dependency>
                ...
                <dependency>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                    <version>{vintage-version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>
...
<dependencies>
    ...
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>{junit4-version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>
...

4.2.3. 単一のテストクラスを実行

JUnit Platrform Surefire Providerは、Maven Surefire Pluginによってサポートされている test JVMシステムプロパティをサポートしています。 例えば、 org.example.MyTest 内のテストメソッドだけを実行するには、 コマンドラインから mvn -Dtest=org.example.MyTest test を実行してください。 さらなる詳細については、 Mavne Surefire Pluginのドキュメントを参照してください。

4.2.4. テストクラス名によるフィルタリング

Maven Surefire Pluginは次のパターンにマッチする完全修飾名を持つテストクラスを スキャンします。

  • */Test.java

  • **/*Test.java

  • **/*Tests.java

  • **/*TestCase.java

さらに、全てのネストされたクラス(静的なメンバークラスも含む)はデフォルトでは除外されます。

しかしながら、このデフォルトの振る舞いは、 includeexclude の規則を pom.xml 明示的に設定にすることで、上書きできます。 例えば、Maven Surefireに静的なメンバークラスを除外させないためには、除外規則を上書きします。

Maven Surefireの除外規則の上書き
 ...
<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>{surefire-version}</version>
            <configuration>
                <excludes>
                    <exclude/>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>
...

詳細については、Maven Surefireの テストの包含と除外 のドキュメントをご覧ください。

タグによるフィルタリング

次の設定プロパティを用いることで、タグまたは タグ表現 によって テストをフィルターすることができます。

  • タグ または タグ表現 を算入するには、 groups または includeTags を使ってください。

  • タグ または タグ表現 を除外するには、 excludedGroups または excludeTags を使ってください。

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.21.0</version>
            <configuration>
                <properties>
                    <includeTags>acceptance | !feature-a</includeTags>
                    <excludeTags>integration, regression</excludeTags>
                </properties>
            </configuration>
            <dependencies>
                ...
            </dependencies>
        </plugin>
    </plugins>
</build>
...
設定パラメータ

configurationParameters プロパティを宣言し、 (下に示すように)Java Properties ファイル構文を使うか junit-platform.properties ファイルを通してキーバリューペアを提供することで、 テスト探索と実行に影響を与えるJUnit Platform 設定パラメータ をセットすることができます。

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.21.0</version>
            <configuration>
                <properties>
                    <configurationParameters>
                        junit.jupiter.conditions.deactivate = *
                        junit.jupiter.extensions.autodetection.enabled = true
                        junit.jupiter.testinstance.lifecycle.default = per_class
                    </configurationParameters>
                </properties>
            </configuration>
            <dependencies>
                ...
            </dependencies>
        </plugin>
    </plugins>
</build>
...

4.2.5. Ant

Ant のバージョン 1.10.3 から、 新しい junitlauncher タスクが 導入され、JUnit Platform上でのテスト実行がネイティブサポートされました。 junitlauncher タスクは単独で、JUnit Platformを起動して選択されたテストコレクションを 渡します。JUnit Platformはその後、テストの探索と実行を登録されたテストエンジンに委譲します。

junitlauncher タスクは、 リソースコレクション といったネイティブなAnt構造になるべく密接に連携しています。 これにより、ユーザがテストエンジンに実行してほしいテストを選択する際、 他多数のコアなAntタスクと比べて、一貫して自然な感覚を与えています。

Ant 1.10.3にある junitlauncher タスクのバージョンは、 JUnit Platformを起動するための基本的で最小のサポートを提供しています。 追加的な機能強化(分離したJVMへのテスト分岐のサポートも含む)が 次のAntのリリースで利用可能になる予定です。

junit5-jupiter-starter-ant プロジェクト内の build.xml は、 開始ポイントとしてタスクの利用方法を示しています。

基本的な使い方

次の例は、 junitlauncher タスクに1つのテストクラス (つまり、 org.myapp.test.MyFirstJUnit5Test )を選ぶための設定方法を示しています。

<path id="test.classpath">
    <!-- The location where you have your compiled classes -->
    <pathelement location="${build.classes.dir}" />
</path>

<!-- ... -->

<junitlauncher>
    <classpath refid="test.classpath" />
    <test name="org.myapp.test.MyFirstJUnit5Test" />
</junitlauncher>

test 要素を使うことで、実行したいテストを1つ選択できます。 classpath 要素はを使うことで、JUnit Platformを起動するために使うクラスパスを指定できます。 このクラスパスは、実行に加えるテストクラスを配置するためにも使われます。

次の例は、複数の位置からテストクラスを選ぶための junitlauncher タスクの 設定方法を示しています。

<path id="test.classpath">
    <!-- The location where you have your compiled classes -->
    <pathelement location="${build.classes.dir}" />
</path>
....
<junitlauncher>
    <classpath refid="test.classpath" />
    <testclasses outputdir="${output.dir}">
        <fileset dir="${build.classes.dir}">
            <include name="org/example/**/demo/**/" />
        </fileset>
        <fileset dir="${some.other.dir}">
            <include name="org/myapp/**/" />
        </fileset>
    </testclasses>
</junitlauncher>

上の例では、 testclasses 要素によって、異なる位置にある複数のテストクラスを選択しています。

用法と設定オプションのさらなる詳細については、 `junitlauncher`タスク に関するAntの公式ドキュメントをご覧ください。

4.3. コンソールラウンチャー

ConsoleLauncher はコマンドラインJavaアプリケーションで、 JUnit Platformをコンソールから起動できます。 例えば、JUnit VintageとJUnit Jupiterテストを実行し、 テストの実行結果をコンソールに出力します。

全ての依存関係が含まれた実行可能な junit-platform-console-standalone-1.2.0.jar は、 Mavenセントラルレポジトリ内の junit-platform-console-standaloneディレクトリ以下に公開されています。 スタンドアローンな ConsoleLauncher は次のように 実行できます。

java -jar junit-platform-console-standalone-1.2.0.jar <Options>

これが出力の例です。

├─ JUnit Vintage
│  └─ example.JUnit4Tests
│     └─ standardJUnit4Test ✔
└─ JUnit Jupiter
   ├─ StandardTests
   │  ├─ succeedingTest() ✔
   │  └─ skippedTest() ↷ for demonstration purposes
   └─ A special test case
      ├─ Custom test name containing spaces ✔
      ├─ ╯°□°)╯ ✔
      └─ 😱 ✔

Test run finished after 64 ms
[         5 containers found      ]
[         0 containers skipped    ]
[         5 containers started    ]
[         0 containers aborted    ]
[         5 containers successful ]
[         0 containers failed     ]
[         6 tests found           ]
[         1 tests skipped         ]
[         5 tests started         ]
[         0 tests aborted         ]
[         5 tests successful      ]
[         0 tests failed          ]
終了コード
ConsoleLauncher は、いずれかのコンテナまたはテストが失敗した場合は ステータスコード 1 、そうでない場合はステータスコード 0 を返します。

4.3.1. オプション

オプション                                       説明
------                                         -----------
-h, --help                                     ヘルプ情報を表示します。
--disable-ansi-colors                          出力でANSIカラーを無効化します。(全ての
                                                 ターミナルではサポートされていません。)
--details <[none,summary,flat,tree,verbose]    テスト実行時の出力詳細モードを選択します。
  >                                              次のうち、どれか1つを使います: [none, summary, flat,
                                                 tree, verbose]. 'none'が選択された場合、
                                                 サマリーとテスト失敗のみが表示されます。 (default: tree)
--details-theme <[ascii,unicode]>              テスト実行時の出力詳細ツリーのテーマを選択します。
                                                 次のうち、どれか1つを使います: [ascii, unicode]
                                                 (default: unicode)
--class-path, --classpath, --cp <Path:         追加的なクラスパスエントリを提供します。 --
  path1:path2:...>                               例えば、テストエンジンとその依存関係を追加でき
                                                 ます。このオプションは繰り返し可能です。
--reports-dir <Path>                           特定のローカルディレクトリへのレポート出力を有効化
                                                 します。(ディレクトリが存在しない場合は生成されます。)
--scan-modules                                 実験的:テスト発見で用いる全ての解決済みモジュールを
                                                 スキャンします。
-o, --select-module <String: module name>      実験的:テスト発見で用いる1つのモジュールを
                                                 選択します。このオプションは繰り返し可能です。
--scan-class-path, --scan-classpath [Path:     クラスパスまたは明示的なクラスパスのルート上の
  path1:path2:...]                               全てのディレクトリをスキャンします。引数がない場合、
                                                 -cp(ディレクトリとJARファイル)を通して供給された
                                                 追加的なクラスパスエントリと同様にシステムクラスパス
                                                 のみがスキャンされます。
                                                 クラスパス上にない明示的なクラスパスのルートは、
                                                 静かに無視されます。このオプションは繰り返し可能です。
-u, --select-uri <URI>                         テスト発見のためのURIを選択します。
                                                 このオプションは繰り返し可能です。
-f, --select-file <String>                     テスト発見のためのファイルを選択します。
                                                 このオプションは繰り返し可能です。
-d, --select-directory <String>                テスト発見のためのディレクトリを選択します。
                                                 このオプションは繰り返し可能です。
-p, --select-package <String>                  テスト発見のためのパッケージを選択します。
                                                 このオプションは繰り返し可能です。
-c, --select-class <String>                    テスト発見のためのクラスを選択します。
                                                 このオプションは繰り返し可能です。
-m, --select-method <String>                   テスト発見のためのメソッドを選択します。
                                                 このオプションは繰り返し可能です。
-r, --select-resource <String>                 テスト発見のためのクラスパス・リソースを選択します。
                                                 このオプションは繰り返し可能です。
-n, --include-classname <String>               完全修飾名がマッチするクラスのみを内包するための
                                                 正規表現を提供します。不要なクラス読み込みを避ける
                                                 ために、デフォルトのパターンは、クラス名が"Test"で始まるか、
                                                 "Test"または"Tests"で終わるクラスのみを内包
                                                 します。このオプションが繰り返し使われる場合、全ての
                                                 パターンはORを使って結合されます。
                                                 (default: ^(Test.*|.+[.$]Test.*|.*Tests?)$)
-N, --exclude-classname <String>               完全修飾名がマッチするクラスのみを除外するための
                                                 正規表現を提供します。このオプションが繰り返し使われる
                                                 場合、全てのパターンはORを使って結合されます。
--include-package <String>                     テスト実行に含まれるパッケージを提供します。
                                                 このオプションは繰り返し可能です。
--exclude-package <String>                     テスト実行から除外するパッケージを提供します。
                                                 このオプションは繰り返し可能です。
-t, --include-tag <String>                     タグがマッチするテストのみを内包するための
                                                 タグまたはタグ表現を提供します。このオプションが繰り返し
                                                 使われる場合、全てのパターンはORを使って結合され
                                                 ます。
-T, --exclude-tag <String>                     タグがマッチするテストのみを除外するための
                                                 タグまたはタグ表現を提供します。このオプションが繰り返し
                                                 使われる場合、全てのパターンはORを使って結合され
                                                 ます。
-e, --include-engine <String>                  テスト実行で内包するエンジンのIDを提供します。
                                                 このオプションは繰り返し可能です。
-E, --exclude-engine <String>                  テスト実行から除外するエンジンのIDを提供します。
                                                 このオプションは繰り返し可能です。
--config <key=value>                           テスト発見・実行のための設定パラメータをセットします。
                                                 このオプションは繰り返し可能です。

4.4. JUnit 4を用いてJUnit Platformを実行

JUnitPlatform ランナーは、JUnit 4ベースの Runner であり、 JUnit 4環境内のJUnit Platform上でサポートされているプログラミングモデルを持つ テスト(例えば、JUnit Jupiterテストクラス)は全て実行できます。

@RunWith(JUnitPlatform.class) をクラスに付与することで、 JUnit 4をサポートしているもののJUnit Platformはまだサポートしていない IDEやビルドシステムであっても実行できます。

JUnit PlatformはJUnit 4にはない特徴を持っているので、 そのランナーはJUnit Platformのサブセットのみをサポートしています。 特に、レポート機能についてが当てはまります( 表示名vs技術的な名称 をご覧ください)。 しかし、さしあたり JUnitPlatform ランナーはとっかかりやすい方法です。

4.4.1. セットアップ

次のアーティファクトとそれらの依存関係がクラスパス上に必要です。 グループIDやアーティファクトID、バージョンに関する詳細は 依存関係のメタデータ をご覧ください。

明示的な依存関係
  • test スコープ内での junit-platform-runner : JUnitPlatform ランナーの位置

  • test スコープ内での junit-4.12.jar : JUnit 4を用いたテスト実行

  • test スコープ内での junit-jupiter-api : @Test などを含むJUnit Jupiterを用いてテストを書くためのAPI

  • test runtime スコープ内での junit-jupiter-engine : JUnit Jupiterのための`TestEngine`実装

推移的な依存関係
  • test スコープ内での junit-platform-suite-api

  • test スコープ内での junit-platform-launcher

  • test スコープ内での junit-platform-engine

  • test スコープ内での junit-platform-commons

  • test スコープ内での opentest4j

4.4.2. 表示名vs技術的な名称

@RunWith(JUnitPlatform.class) を通して実行するクラスに カスタム 表示名 を定義するには、単に @SuiteDisplayName を クラスに付与してカスタム値を提供するだけです。

デフォルトでは、 表示名 はテストアーティファクトのために使われます。 しかしながら、GradleやMavenといったビルドツールでテスト実行するために JUnitPlatrform ランナーが使われる場合、生成されたテストレポートはしばしば、 テストクラス名や特殊文字を含むカスタム表示名のような短い表示名の代わりに、 テストアーティファクトの 技術的な名称 (例えば、完全修飾クラス名)を含む必要があります。 レポート目的のために技術的な名称を有効化するには、 単に @RunWith(JUnitPlatform.class) と一緒に @UseTechnicalNames アノテーションを宣言するだけです。

@UseTechnicalNames の存在は、 @SuiteDisplayName を通して設定された いかなるカスタム表示名も上書きすることに注意してください。

4.4.3. 単一のテストクラス

JUnitPlatform ランナーを使う1つの方法は、 テストクラスに直接 @RunWith(JUnitPlatform.class) を付与することです。 次の例にあるテストメソッドは org.junit.Test (JUnit Vintage)ではなく、 org.junit.jupiter.api.Test (JUnit Jupiter)が付与されていることに注意してください。 さらにこの場合では、テストクラスは public でないといけません。 そうでないと、IDEやビルドツールがJUnit 4のテストクラスとして認識しない恐れがあります。

import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
public class JUnit4ClassDemo {

    @Test
    void succeedingTest() {
        /* no-op */
    }

    @Test
    void failingTest() {
        fail("Failing for failing's sake.");
    }

}

4.4.4. テストスイート

複数のテストクラスがある場合、次の例のようにテストスイートを作成できます。

import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.SuiteDisplayName;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
@SuiteDisplayName("JUnit 4 Suite Demo")
@SelectPackages("example")
public class JUnit4SuiteDemo {
}

JUnit4SuiteDemo は、 example パッケージとそのサブパッケージ内にある 全てのテストを発見・実行します。デフォルトでは、 Test で始まるか、 Test または Tests で終わる名前を持つテストクラスのみが含まれます。

追加的な設定オプション
@SelectPackages だけでなく、テスト発見・フィルタリング用の 多くの設定オプションがあります。詳細については、 Javadocをご覧ください。

4.5. 設定パラメータ

どのテストクラスとテストエンジンを内包するか、どのパッケージをスキャンするか、 などをプラットフォームに知らせるのに加えて、特定のテストエンジンや登録した拡張を 指定するための追加的なカスタム設定パラメータの提供が必要な場合があります。 例えば、JUnit Jupiter TestEngine は次のユースケースのための 設定パラメータ を サポートしています。

設定パラメータ はテキストベースのキーバリューペアで、次の仕組みの内の1つを通して JUnit Platform上で実行しているテストエンジンに供給されます。

  1. configurationParameter()configurationParameters() メソッド。 Launcher API に供給するリクエストを構築する LauncherDiscoveryRequestBuilder 内に実装されています。 JUnit Platformによって提供されているツールの内の1つを通してテスト実行している場合、 次のように、設定パラメータを指定できます。

  2. JVMシステムプロパティ。

  3. JUnit Platform設定ファイル: junit-platform.properties という名前で クラスパスのルートに置かれたJavaの Properties ファイルの構文規則に従ったファイル。

設定パラメータは上で定義された順序で探索されます。結果として、 Launcher に直接供給された設定パラメータは、システムプロパティと設定ファイルを通して供給されたパラメータよりも優先して使われます。同じように、システムプロパティを通して供給された設定パラメータは、設定ファイルを通して供給された設定パラメータよりも優先して使われます。

4.6. タグ表現

タグ表現は、ブーリアン(boolean)表現で、 !&| オペレータが使われます。 さらに、 () もオペレータの優先順位を調節するために使われます。

Table 1. 表1. オペレータ(優先順位の降順)
オペレータ 意味 結合性

!

not

&

and

|

or

いくつかの観点でテストにタグ付けしている場合、タグ表現は実行するテストの選択を助けてくれます。 テストタイプ(例えば、 microintegrationend-to-end )や 特徴( foobarbaz )によってタグ付けするときは、 次のタグ表現が役に立つでしょう。

タグ表現 選択

foo

foo の全てのテスト

bar | baz

bar の全てのテストに加えて baz の全てのテスト

bar & baz

barbaz 相互に関わる全てのテスト

foo & !end-to-end

foo のうち、 end-to-end でない全てのテスト

(micro | integration) & (foo | baz)

foo または baz のうち、 micro または integration の全てのテスト

5. 拡張モデル

5.1. 概要

JUnit 4において競合している Runner@Rule@ClassRule の 拡張ポイントとは対照的に、JUnit Jupiterの拡張モデルは単一の包括的なコンセプト: Extension APIからなっています。しかしながら、 Extension 自体はただの マーカーインターフェイスであることに注意してください。

5.2. 拡張の登録

拡張は、 @ExtendWith を通して 宣言的に 、 または @RegisterExtension を通して プログラム的に 、 もしくはJavaの ServiceLoader 機構を通して 自動的に 登録されます。

5.2.1. 宣言的な拡張登録

テストインターフェイスまたはテストクラス、テストメソッド、カスタム 合成アノテーション@ExtendWith(…​) を付与し、登録する拡張のクラス参照を供給することで、 1つ以上の拡張を 宣言的に 登録できます。

例えば、特定のテストメソッドのためにカスタム RandomParametersExtension を 登録するには、次のようにテストメソッドにアノテーションを付与します。

@ExtendWith(RandomParametersExtension.class)
@Test
void test(@Random int i) {
    // ...
}

特定のクラスとそのサブクラスの全てのテストにカスタム RandomParametersExtension を 登録するには、次のようにテストクラスにアノテーションを付与します。

@ExtendWith(RandomParametersExtension.class)
class MyTests {
    // ...
}

複数の拡張はこのように一緒に登録できます:

@ExtendWith({ FooExtension.class, BarExtension.class })
class MyFirstTests {
    // ...
}

他には、複数の拡張はこのように分けて登録できます:

@ExtendWith(FooExtension.class)
@ExtendWith(BarExtension.class)
class MySecondTests {
    // ...
}
拡張登録の順序
@ExtendWith を通した宣言的に登録された拡張は、 ソースコード内で宣言された順番で実行されます。 例えば、 MyFirstTestsMySecondTests のテストの実行は、 FooExtensionBarExtension によって、 正確にこの順番で 拡張されます。

5.2.2. プログラム的な拡張登録

テストクラスのフィールドに @RegisterExtension を付与することで、 プログラム的に 拡張を登録できます。

@ExtendWith を通して 宣言的に 拡張を登録した場合、概してアノテーション通りにのみ設定できます。 それに対して、 @RegisterExtension を通して拡張を登録した場合、 プログラム的に 拡張することができます。 例えば、拡張のコンストラクタや静的なファクトリメソッド、ビルダーAPIに 引数を渡すために使うことができます。

@RegisterExtension フィールドは、 privatenull(評価時)であってはなりませんが、 static であっても非静的であっても構いません。
静的なフィールド

@RegisterExtension フィールドが static である場合、 @ExtendWith を通したクラスレベルでの拡張が登録された後に、拡張は登録されます。 そのような 静的な拡張 は、拡張API内で実装できることを制限するものではありません。 静的なフィールドを通して登録された拡張はそのため、 BeforeEachCallback のような メソッドレベルの拡張APIと同様に、 BeforeAllCallbackAfterAllCallbackTestInstancePostProcessor のようなクラスレベルとインスタンスレベルの拡張APIを実装します。

次の例では、テストクラスの server フィールドが、 WebServerExtension によって サポートされているビルダーパターンを用いてプログラム的に初期化されます。 設定された WebServerExtension は、クラスレベルの拡張として自動的に登録されます。 例えば、クラスの全てのテストの実行前にサーバを起動し、実行完了後にサーバを停止するためです。 さらに、 @BeforeEach@AfterEach@Test メソッドだけでなく、 @BeforeAll@AfterAll が付与された静的なライフサイクルメソッドは、 必要であれば、 server フィールドを通して拡張のインスタンスにアクセスできます。

静的なフィールドを通して登録された拡張
class WebServerDemo {
    @RegisterExtension
    static WebServerExtension server = WebServerExtension.builder()
        .enableSecurity(false)
        .build();

    @Test
    void getProductList() {
        WebClient webClient = new WebClient();
        String serverUrl = server.getServerUrl();
        // Use WebClient to connect to web server using serverUrl and verify response
        assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
    }
}
インスタンスフィールド

@RegisterExtension フィールドが非静的(つまり、インスタンスフィールド)である場合、 テストクラスが初期化された後、さらに、登録された各 TestInstancePostProcessor が テストインスタンスを事後処理されてから (使われる拡張のインスタンスをアノテーションが付与されたフィールドに潜在的に挿入します)、 拡張は登録されます。そのため、そのような インスタンス拡張BeforeAllCallbackAfterAllCallbackTestInstancePostProcessor のようなクラスレベル、 もしくはインスタンスレベルの拡張APIを実装している場合、それらのAPIは評価されません。 デフォルトでは、 @ExtendWith を通したメソッドレベルで登録された拡張の 後に 、 インスタンス拡張は登録されます。しかしながら、テストクラスが @TestInstance(Lifecycle.PER_CLASS) セマンティックで設定されている場合、 @ExtendWith を通したメソッドレベルの拡張が登録される 前に 、 インスタンス拡張は登録されます。

次の例では、テストクラスの docs フィールドが、カスタム lookUpDocsDir() メソッドを 呼び出し、その結果を DocumentationExtension の静的な forPath() ファクトリメソッドに 供給することで、プログラム的に初期化されます。設定された DocumentationExtension は、 メソッドレベルの拡張として自動的に登録されます。さらに、 @BeforeEach@AfterEach@Test メソッドは、必要であれば、 docs フィールドを通して拡張のインスタンスにアクセスできます。

インスタンスフィールドを通して登録された拡張
class DocumentationDemo {

    static Path lookUpDocsDir() {
        // return path to docs dir
    }

    @RegisterExtension
    DocumentationExtension docs = DocumentationExtension.forPath(lookUpDocsDir());

    @Test
    void generateDocumentation() {
        // use this.docs ...
    }
}

5.2.3. 自動的な拡張登録

アノテーションを用いた 宣言的な拡張登録プログラム的な拡張登録 のサポートに加えて、 JUnit JupiterはJavaの java.util.ServiceLoader 機構を通じた グローバル拡張登録 もサポートしており、サードパーティによる拡張を自動で検出し、 クラスパス上で利用可能なものを元に自動で登録します。

特に、カスタム拡張は、同封するJARファイルの中で /META-INF/services フォルダ内の org.junit.jupiter.api.extension.Extension という名前のファイルで 完全修飾クラス名を供給することによって、登録することができます。

自動拡張検出の有効化

自動検出は先進的な特徴で、そのためデフォルトでは有効化されていません。 有効化するには、単に junit.jupiter.extensions.autodetection.enabled 設定パラメータtrue をセットするだけです。これは、JVMシステムプロパティや Launcher に渡される LauncherDiscoveryRequest 内の 設定パラメータ として、 またはJUnit Platform設定ファイル(詳細は 設定パラメータ をご覧ください)を通しても供給することが可能です。

例えば、拡張の自動検出を有効にするには、JVMを次のシステムプロパティで起動します。

-Djunit.jupiter.extensions.autodetection.enabled=true

自動検出が有効化されている場合、JUnit JUpiterのグローバル拡張 (つまり TestInfoTestReporter などへのサポート)の後に、 ServiceLoader 機構を通して発見された拡張は拡張レジストリに追加されます。

5.2.4. 拡張の継承

登録された拡張は、トップダウンに従ってテストクラス階層の中で継承されます。 同様に、クラスレベルで登録された拡張は、メソッドレベルで継承されます。 さらに、特定の拡張実装は、ある拡張コンテキストと親コンテキストに対して1度だけ登録されます。 その結果として、重複する拡張実装の登録は無視されます。

5.3. 条件付きテスト実行

ExecutionCondition は、プログラム的な 条件付きテスト実行 のための Extension APIを定義しています。

ExecutionCondition は、各コンテナ(つまり、テストクラス)が含む全てのテストを 実行すべきか、供給された ExecutionContext に則って決定するために 評価 されます。 同じように ExecutionCondition は、各テストごとに供給された ExecutionContext に則って 実行されるか決定するために 評価 されます。

複数の ExecutionCondition が登録されている場合、コンテナもしくはテストは、 条件のうち1つでも disabled を返した瞬間に無効化されます。 そのため、条件が評価されるかどうかの保証はありません。 なぜなら、他の条件が既にコンテナもしくはテストを無効化している可能性があるからです。 つまり、評価は短絡ブーリアンORオペレータのように作動します。

具体的な例については、 DisabledCondition@Disabled のソースコードをご覧ください。

5.3.1. 条件の無効化

時には、いくつかの条件を有効に しないで テストスイートを実行することが役に立ちます。 例えば、 @Disabled がたとえ付与されていたとしても、それらが 壊れていないか 確認するために実行したいかもしれません。 このためには、単に junit.jupiter.conditions.deactivate 設定パラメータ に 現在のテスト実行でどの条件を無効化する(つまり、評価しない)かを指定するパターンを提供するだけです。 パターンは、JVMシステムプロパティや、 Launcher に渡される LauncherDiscoveryRequest 内の 設定パラメータ として、もしくはJUnit Platform設定ファイル (詳細は 設定パラメータ をご覧ください)を 通して供給することができます。

例えば、JUnitの @Disabled 条件を無効化するには、 次のシステムプロパティでJVMを起動します。

-Djunit.jupiter.conditions.deactivate=org.junit.*DisabledCondition

パターンマッチングの構文

junit.jupiter.conditions.deactivate パターンが、アスタリスク( * )のみで 構成されている場合、全ての条件が無効化されます。そうでない場合、パターンは、 各登録された条件の完全修飾クラス名( FQCN )とマッチしているかの判断に使われます。 パターン内の全てのドット( . )は、FQCN内のドット( . )またはドル( $ )と マッチします。全てのアスタリスク( * )は、FQCN内の1つ以上の文字とマッチします。 パターン内の他の全ての文字は、FQCNと1対1でマッチします。

例:

  • * :全ての条件を無効化します。

  • org.junit.*org.junit パッケージとサブパッケージ以下の全ての条件を無効化します。

  • *.MycCondition:クラス名が MyCondition のものを無効化します。

  • System :クラス名に System を含むものを無効化します。

  • org.example.MyCondition :FQCNが org.example.MyCondition のものを無効化します。

5.4. テストインスタンスの事後処理

TestInstancePostProcessor は、テストインスタンスを 事後処理 したい Extensions のAPIを定義しています。

一般的なユースケースには、テストインスタンスに依存関係を挿入したり、 テストインスタンス上でカスタム初期化メソッドを呼び出すことなどが含まれます。

具体的な例については、 MockitoExtensionSpringExtension のソースコードをご覧ください。

5.5. パラメータの解決

ParameterResolver は、実行時にパラメータを動的に解決するための Extension APIを定義しています。

テストコンストラクタまたは @Test@RepeatedTest@ParameterizedTest@TestFactory@BeforeEach@AfterEach@BeforeAll@AfterAll`メソッドが パラメータを受け入れている場合、パラメータは `ParameterResolver によって実行時に 解決される 必要があります。 ParameterResolver`は 、組み込みもの( `https://github.com/junit-team/junit5/tree/{release-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java[TestInfoParameterResolver] をご覧ください) まはた ユーザが登録したもの を使うことができます。 一般的には、パラメータは 名前アノテーション 、それらの組み合わせで解決されます。 CustomTypeParameterResolverCustomAnnotationParameterResolver のソースコードをご覧ください。

JDK 9より前のバージョンのJDKの javac によって生成されるバイトコード内の バグによって、コアの java.lang.reflect.Parameter APIを通したパラメータに対する アノテーションの直接の探索は、 内部クラス のコンストラクタ (例えば、 @Nested テストクラス内のコントラクタ)は常に失敗します。

ParameterContext APIは、そのため、パラメータ上のアノテーションの正しい探索のために、 次の便利なメソッドを含んでいます。拡張に関する開発者(Extension Authors)は、 JDK内のこのバグを避けるために、 java.lang.reflect.Parameter 内で提供されている メソッドの代わりに、これらのメソッドを使うことが強く奨励されています。

  • boolean isAnnotated(Class<? extends Annotation> annotationType)

  • Optional<A> findAnnotation(Class<A> annotationType)

  • List<A> findRepeatableAnnotations(Class<A> annotationType)

5.6. テストライフサイクルのコールバック

次のインターフェイスは、テスト実行ライフサイクルにおいて様々なポイントで テストを拡張するためのAPIを定義しています。 実例に関しては次の章を、さらなる詳細については org.junit.jupiter.api.extension パッケージ内の各インターフェイスのJavadocをご覧ください。

複数の拡張APIsの実装
拡張に関する開発者は、これらのインターフェイスのうちいくつかを1つの拡張内に 実装できます。具体的な例については、 SpringExtension のソースコードをご覧ください。

5.6.1. BeforeとAfterのテスト実行コールバック

BeforeTestExecutionCallbackAfterTestExecutionCallback はそれぞれ、 テストメソッドが実行される 直前直後 に実行される振る舞いを追加するための Extension APIを定義しています。そのように、これらのコールバックは、 タイミングよく跡を追う似たようなユースケースによく適しています。 @BeforeEach@AfterEach メソッドの 周り で呼び出されるコールバックを 実装する必要がある場合、代わりに BeforeEachCallbackAfterEachCallback を 実装してください。

次の例は、これらのコールバックを使ってテストメソッドの実行時間を計算しログする方法を示しています。 TimingExtension は、テスト実行の時間計測とログのために BeforeTestExecutionCallbackAfterTestExecutionCallback を実装しています。

テストメソッドの実行を時間計測してログする拡張
import java.lang.reflect.Method;
import java.util.logging.Logger;

import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;

public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    private static final Logger logger = Logger.getLogger(TimingExtension.class.getName());

    private static final String START_TIME = "start time";

    @Override
    public void beforeTestExecution(ExtensionContext context) throws Exception {
        getStore(context).put(START_TIME, System.currentTimeMillis());
    }

    @Override
    public void afterTestExecution(ExtensionContext context) throws Exception {
        Method testMethod = context.getRequiredTestMethod();
        long startTime = getStore(context).remove(START_TIME, long.class);
        long duration = System.currentTimeMillis() - startTime;

        logger.info(() -> String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
    }

    private Store getStore(ExtensionContext context) {
        return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));
    }

}

TimingExtensionTests クラスは、 @ExtendWith を通して TimingExtension を 登録しているので、そのテストは実行時にこの時間計測が適用されます。

TimingExtensionを用いるテストクラス
@ExtendWith(TimingExtension.class)
class TimingExtensionTests {
    @Test
    void sleep20ms() throws Exception {
        Thread.sleep(20);
    }

    @Test
    void sleep50ms() throws Exception {
        Thread.sleep(50);
    }
}

次にあるのは、 TimingExtensionTests が実行された時に生成されたログの例です。

INFO: Method [sleep20ms] took 24 ms.
INFO: Method [sleep50ms] took 53 ms.

5.7. 例外処理

TestExecutionExceptionHandler は、テスト実行中に投げられた例外を処理する Extensions のためのAPIを提供しています。

次の例は、 IOException の全インスタンスを飲み込み、他の型の例外を投げ直す拡張を示しています。

例外を扱う拡張
public class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {
    @Override
    public void handleTestExecutionException(ExtensionContext context, Throwable throwable)
            throws Throwable {

        if (throwable instanceof IOException) {
            return;
        }
        throw throwable;
    }
}

5.8. テストテンプレートに関する呼び出しコンテキストの提供

@TestTemplate メソッドは、少なくとも1つの TestTemplateInvocationContextProvider が 登録されているときにだけ実行されます。 各プロバイダは、 TestTemplateInvocationContext インスタンスの Stream を提供する責務を担っています。 各コンテキストは、カスタム表示名と @TestTemplate メソッドの次の呼び出しのために使われる追加的な拡張のリストを特定します。

次の例は、テストテンプレートの書き方だけでなく、TestTemplateInvocationContextProvider の登録・実装の方法も示しています。

付随する拡張を持つテストテンプレート
@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String parameter) {
    assertEquals(3, parameter.length());
}

public class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        return Stream.of(invocationContext("foo"), invocationContext("bar"));
    }

    private TestTemplateInvocationContext invocationContext(String parameter) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return parameter;
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return Collections.singletonList(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameterContext.getParameter().getType().equals(String.class);
                    }

                    @Override
                    public Object resolveParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameter;
                    }
                });
            }
        };
    }
}

この例では、テストテンプレートは2回呼び出されます。 呼び出しの表示名は、呼び出しコンテキストによって決められたように "foo" と "bar" になります。 各呼び出しは、メソッドパラメータを解決するためのカスタム ParameterResolver を登録します。 ConsoleLauncher を使った時の出力は次のようになります。

└─ testTemplate(String) ✔
   ├─ foo ✔
   └─ bar ✔

TestTemplateInvocationContextProvider 拡張APIは、 異なるコンテキストでテストのようなメソッドを繰り返し呼び出すことを当てにしている 別種のテストを実装することを第一に意図しています。 例えば、異なるパラメータを用いたり、テストクラスインスタンスを異なる方法で準備したり、 コンテキストを修正せずに複数回行うなどです。 機能に提供ためにこの拡張ポイントを使う 繰り返しテスト または パラメータ化テスト の実装を参照してください。

5.9. 拡張での状態の保持

通常、拡張は一度だけインスタンス化されます。 そのため、このような関連する質問が挙がります:拡張のある呼び出しからの状態はどのようにして次の呼び出しに保持されるのか? ExtensionContext APIは、まさにこの目的のための Store を提供します。 拡張は、後からの収集のためにストアに値を入れます。 メソッドレベルのスコープでの Store の使用例は、 タイミング拡張 をご覧ください。 テスト実行中に ExtensionContext に貯蔵された値は、 周囲の ExtensionContext では利用できないことに注意してください。 ExtensionContext はネストされているかもしれないので、 内部コンテキストのスコープもまた限定的となっています。 Store を通した値の貯蔵と収集に利用可能なメソッドの詳細については、 対応するJavaDocをご覧ください。

ExtensionContext.Store.CloseableResource
拡張コンテキストのストアは、その拡張コンテキストのライフサイクルにバインドされています。 拡張コンテキストのライフサイクルが終了するとき、関連するストアも閉じます。 全ての貯蔵された値は、 CloseableResource のインスタンスで、 close() メソッドの呼び出しによって通知されます。

5.10. 拡張でサポートしているユーティリティ

junit-platform-commons アーティファクトは、アノテーションやクラス、 リフレクション、タスクをスキャンするクラスパスに作動する 保守された ユーティリティメソッドを org.junit.platform.commons.support という名前のパッケージを公開しています。 TestEngineExtension の開発者は、JUnit Platformの動作に合わせるため、 これらサポートされたメソッドを利用することが推奨されています。

5.10.1. アノテーションのサポート

AnnotationSupport は、静的なユーティリティメソッドを提供しており、 アノテーションの付与された要素(つまり、パッケージやアノテーション、クラス、インターフェイス、 メソッド、フィールド)を操作できます。 これらは、ある要素に特定のアノテーションが付与またはメタ付与されているか確認したり、 特定のアノテーションを検索したり、クラスやインターフェイス内でアノテーション付与されている メソッドとフィールドを探し出すためのメソッドが含まれています。 これらのメソッドのいくつかは、実装されたインターフェイスとクラス階層内を アノテーションを見つけるために捜索します。 さらなる詳細については、 AnnotationSupport のJavaDocをご覧ください。

5.10.2. クラスのサポート

ClassSupport は、クラス(つまり、 java.lang.Class のインスタンス)に 作動する静的なユーティリティメソッドを提供しています。 さらなる詳細については、 ClassSupport のJavaDocをご覧ください。

5.10.3. リフレクションのサポート

ReflectionSupport は、標準のJDKリフレクションとクラス読み込み機構を増強するための 静的なユーティリティメソッドを提供しています。 これらは、特定の述語にマッチするクラスを探すためにクラスパスをスキャンしたり、 クラスの新しいインスタンスを読み込んで生成したり、 メソッドを見つけて呼び出すためのメソッドを含みます。 これらのメソッドのいくつかは、マッチするメソッドの位置を特定するためにクラス階層を走査します。 さらなる詳細については、 ReflectionSupport のJavaDocをご覧ください。

5.11. ユーザコードと拡張の相対実行順序

1つ以上のテストメソッドを含むテストクラスを実行する時、 ユーザの提供するテストとライフサイクルに関するメソッドに加えて、 いくつかの拡張コールバックが呼び出されます。 次の図は、ユーザ提供のコードと拡張コードの相対順序を示しています。

extensions lifecycle
User code and extension code

ユーザ提供のテストとライフサイクルに関するメソッドはオレンジで示され、 拡張によって提供されるコールバックは青で示されています。 灰色のボックスは、1つのテストメソッドの実行を意味しており、 テストクラスの全てのテストメソッドに対して繰り返されます。

次の表は、 ユーザコードと拡張コード の図式内の12のステップについて、より詳しく説明をしています。

ステップ インターフェイス/アノテーション 説明

1

インターフェイス org.junit.jupiter.api.extension.BeforeAllCallback

コンテナの全てのテストが実行される前に実行される拡張コード

2

アノテーション org.junit.jupiter.api.BeforeAll

コンテナの全てのテストが実行される前に実行されるユーザコード

3

インターフェイス org.junit.jupiter.api.extension.BeforeEachCallback

各テストが実行される前に実行される拡張コード

4

アノテーション org.junit.jupiter.api.BeforeEach

各テストが実行される前に実行されるユーザコード

5

インターフェイス org.junit.jupiter.api.extension.BeforeTestExecutionCallback

テストが実行される直前に実行される拡張コード

6

アノテーション org.junit.jupiter.api.Test

実際のテストメソッドとなるユーザコード

7

インターフェイス org.junit.jupiter.api.extension.TestExecutionExceptionHandler

テスト中に投げられる例外を扱う拡張コード

8

インターフェイス org.junit.jupiter.api.extension.AfterTestExecutionCallback

テストと対応する例外ハンドラが実行された直後に実行される拡張コード

9

アノテーション org.junit.jupiter.api.AfterEach

各テストが実行された後に実行されるユーザコード

10

インターフェイス org.junit.jupiter.api.extension.AfterEachCallback

各テストが実行された後に実行される拡張コード

11

アノテーション org.junit.jupiter.api.AfterAll

コンテナの全てのテストが実行された後に実行されるユーザコード

12

インターフェイス org.junit.jupiter.api.extension.AfterAllCallback

コンテナの全てのテストが実行された後に実行される拡張コード

最も単純なケースでは、実際のテストメソッドのみが実行されます(ステップ 6)。 他の全てのステップはオプションで、対応するライフサイクルコールバックをサポートするユーザコードまたは拡張コードの存在に依存します。 様々なライフサイクルコールバックのさらなる詳細については、 各アノテーションと拡張それぞれのJavaDocをご覧ください。

6. JUnit 4からの移行

JUnit Jupiterのプログラミングモデルと拡張モデルは、 JUnit 4の RulesRunners といった特徴をネイティブではサポートしていませんが、 ソースコード保守運用者が全ての既存のテストやテスト拡張、カスタムされたビルド・テスト基盤を JUnit Jupiterに移行するためにアップデートする必要があることは期待していません。

代わりに、JUnitは、JUnit Platformの基盤を用いてJUnit 3とJUnit 4ベースのテストを 実行できる JUnit Vintageテストエンジン を通して、寛大な移行経路を提供しています。 JUnit Jupiterに特化したクラスとアノテーションは全て新しい org.junit.jupiter ベース パッケージ以下にあるので、クラスパス上にあるJUnit 4とJUnit Jupiterは いずれもコンフリクトを起こしません。そのため、JUnit Jupiterテストと一緒に 既存のJUnit 4のテストを保守することは安全です。さらに、JUnitチームはJUnit 4.xベースラインの 保守とバグ修正に関するリリースを提供し続けるので、開発者はスケジュール上では JUnit Jupiter移行のための時間が多くあります。

6.1. JUnit Platform上でのJUnit 4テストの実行

junit-vintage-engine アーティファクトがテストの実行時のパスにあるか確認するだけです。 そのような場合、JUnit 3とJUnit 4のテストは、JUnit Platformラウンチャーによって自動的に拾われます。

GradleとMavenでの実施方法は、 junit5-samples レポジトリのプロジェクト例をご覧ください。

6.1.1. カテゴリのサポート

@Category が付与されたテストクラスとメソッドに関して、 JUnit Vintageテストエンジン はカテゴリの完全修飾クラス名を 対応するテスト識別子のタグとして扱います。 例えば、テストメソッドに @Category(Example.class) が付与されている場合、 "com.acme.Example" のタグが付与されます。 JUnit 4の Categories ランナーと同じように、この情報は発見されたテストが 実行される前にフィルタリングされるのに使われます (詳細については テストを実行する をご覧ください)。

6.2. 移行のためのTips

次は、既存のJUnit 4のテストをJUnit Jupiterに移行する際に気をつけるべきことです。

  • org.junit.jupiter.api パッケージ内のアノテーション

  • org.junit.jupiter.api.Assertions 内のアサーション

  • org.junit.jupiter.api.Assumptions 内のアサンプション

  • @Before@After はもうありません。代わりに @BeforeEach@AfterEach を使ってください。

  • @BeforeClass@AfterClass はもうありません。代わりに @BeforeAll@AfterAll を使ってください。

  • @Ignore はもうありません。代わりに @Disabled を使ってください。

  • @Category はもうありません。代わりに @Tag を使ってください。

  • @RunWith はもうありません。代わりに @ExtendWith を使ってください。

  • @Rule@ClassRule はもうありません。 @ExtendWith によって置換されました。個別のRuleのサポートについては次の章をご覧ください。

6.3. JUnit 4のRuleの限定的なサポート

上で述べたように、JUnit Jupiterは、JUnit 4のRuleを ネイティブではサポートしていませんし、これからもしません。 しかしながら、JUnitチームは、多くの組織(特に、大きい組織)がカスタムRuleを 利用した巨大なJUnit 4のコードベースを持っていることを理解しています。 これらの組織に奉仕し、段階的な移行を可能にするために、 JUnitチームは、言葉通り選択的ですが、いくつかJUnit 4のRuleを JUnit Jupiter内でサポートすることを決めました。 このサポートは、アダプタベースで行われ、JUnit Jupiterの拡張モデルと 意味的に互換のあるRule(つまり、テスト全体の実行フローを完全には変更しないもの)に 限定されています。

JUnit Jupiterの junit-jupiter-migrationsupport モジュールは、 現在のところ、次の3つの Rule 型とそれらのサブクラスをサポートしています。

  • org.junit.rules.ExternalResourceorg.junit.rules.TemporaryFolder を含む)

  • org.junit.rules.Verifierorg.junit.rules.ErrorCollector を含む)

  • org.junit.rules.ExpectedException

JUnit 4と同様に、Ruleが付与されたフィールドもメソッド同様にサポートされています。 これらを使うことで、レガシーコード内の Rule 実装のようなテストクラスに 対するクラスレベルの拡張を、JUnit 4のRuleのインポート文含めて 変更なしで残す ことができます。

この限られた形での Rule サポートは、クラスレベルのアノテーション org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport によって 切り替えできます。このアノテーションは、 合成アノテーション で、 全ての移行をサポートする拡張( VerifierSupportExternalResourceSupportExpectedExceptionSupport )を有効にします。

しかしながら、JUnit 5に対する新たな拡張を開発する際には、 JUnit 4のRuleベースのモデルの代わりに、JUnit Jupiterの新しい拡張モデルを使ってください。

Junit Jupiter内におけるJUnit 4の Rule のサポートは現在のところ、 実験的な 特徴です。詳細については、 実験的なAPI をご覧ください。

7. 先進的なトピック

7.1. JUnit PlatformラウンチャーAPI

JUnit 5の主要な目的の一つは、JUnitとそのプログラム的なクライアント(ビルドツールとIDE)間の インターフェイスをより強固で安定したものにすることです。 その目的は、テスト発見と実行の内部を、外界から必要とされる全てのフィルタリングと 設定から切り離すことです。

JUnit 5は、テスト発見・フィルタリング・実行に使うことのできる Launcher の コンセプトを導入します。さらに、サードパーティによるテストライブラリ (SpockやCucumber、FitNesse)は、カスタム TestEngine を提供することによって JUnit Platformの起動基盤にプラグインできます。

起動APIは junit-platform-launcher モジュールにあります。

起動APIの使用例は、 junit-platform-console プロジェクトの ConsoleLauncher にあります。

7.1.1. テストの発見

テストディレクトリ をプラットフォーム自身専用の特徴として導入することは、 過去にテストクラスやテストメソッドを特定するのに経験しなければならなかった困難の ほとんどからIDEやビルドツールを(上手くいけば)解放するでしょう。

使用例:

import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;

import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
    .selectors(
        selectPackage("com.example.mytests"),
        selectClass(MyTestClass.class)
    )
    .filters(
        includeClassNamePatterns(".*Tests")
    )
    .build();

Launcher launcher = LauncherFactory.create();

TestPlan testPlan = launcher.discover(request);

現在のところ、クラスとメソッド、あるパッケージ内の全てのクラスを選択することや、 クラスパス内の全てのテストを探索することさえ可能です。 発見は、全ての関係するテストエンジンで行われます。

結果として TestPlan は、 LauncherDiscoveryRequest に適合する 全てのエンジンやクラス、テストメソッドの階層的(かつ、読み込み専用)な記述となります。 クライアントは、そのツリーを走査し、ノードに関する詳細を集め、 元ソース(クラスやメソッド、ファイルの位置)へのリンクを入手できます。 テストプランの全てのノードは、 ユニークなID を持っており、 特定のテストやテストグループを呼び出すのに使用できます。

7.1.2. テストの実行

テストを実行するために、クライアントは、発見フェイズと同じ LauncherDiscoveryRequest を 使うか、新しいリクエストを生成できます。 テスト進行とレポートは、次の例のように Launcher に1つ以上の TestExecutionListener 実装を登録することで達成できます。

LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
    .selectors(
        selectPackage("com.example.mytests"),
        selectClass(MyTestClass.class)
    )
    .filters(
        includeClassNamePatterns(".*Tests")
    )
    .build();

Launcher launcher = LauncherFactory.create();

// Register a listener of your choice
TestExecutionListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);

launcher.execute(request);

execute() メソッドに返り値はありませんが、リスナーを用いることで、 容易に自身のオブジェクト内で最終結果を集約することができます。 例については、 SummaryGeneratingListener をご覧ください。

7.1.3. 自身のテストエンジンをプラグイン

JUnitは現在のところ、2つの利用可能な TestEngine を提供しています。

  • junit-jupiter-engine :JUnit Jupiterのコアです。

  • junit-vintage-engine :JUnit 4の上部に位置する薄いレイヤーで、 古い テストを起動基盤で実行できるようにします。

サードパーティもまた、 junit-platform-engine モジュール内のインターフェイスを実装して エンジンを 登録する ことで、自身の TestEngine に貢献する可能性があります。 エンジンの登録は、現在のところ、Javaの java.util.ServiceLoader 機構によって サポートされています。例えば、 junit-jupiter-engine モジュールは、 junit-jupiter-engine JAR中の /META-INF/services 内にある org.junit.platform.engine.TestEngine という名前のファイルの org.junit.jupiter.engine.JupiterTestEngine を登録します。

7.1.4. 自身のテスト実行リスナーをプラグイン

テスト実行リスナーをプログラム的に登録するためのパブリックな Launcher APIメソッドに 加えて、Javaの java.util.ServiceLoader 機能を通して実行時に発見される カスタム TestExecutionListener 実装が自動的に DefaultLauncher を用いて登録されます。 例えば、 TestExecutionListener を実装し、 /META-INF/services/org.junit.platform.launcher.TestExecutionListener ファイル内で宣言されている example.TestInfoPrinter クラスは、 自動的に読み込まれ登録されます。

8. APIの進化

JUnit 5の大きな目標の1つは、多くのプロジェクトに現在進行形で使われていますが、 JUnitを進化させるために保守運用者ができることを改善させることです。 JUnit 4では、元は内側の構成物として加えられていた多くのものが、 外部の拡張開発者やツールビルダーに使われました。 そのことが、JUnit 4を特に難しく、時には不可能なものにしました。

というわけで、JUnit 5は全ての公開されている利用可能なインターフェイスやクラス、メソッドに 対して、あらかじめ定義したライフサイクルを導入します。

8.1. APIのバージョンとステータス

全ての公開されたアーティファクトは、バージョン番号 <メジャー>.<マイナー>.<パッチ> を持ち、 全ての公開されている利用可能なインターフェイスまたはクラス、メソッドは、 @API Guardian プロジェクトから @API を付与されています。 このアノテーションの status 属性は、次の値のうちの1つを割り当てできます。

ステータス 説明

INTERNAL

JUnit自身以外のコードから使われてはなりません。事前通知なしに削除される恐れがあります。

DEPRECATED

もう使うべきではありません。次のマイナーリリースで消滅する恐れがあります。

EXPERIMENTAL

フィードバックを求めている新しい実験的な機能を意図しています。この要素は注意を持って使ってください。 MAINTAINEDSTABLE に将来的には昇格する可能性がありますが、事前通知なしにパッチでも削除される恐れがあります。

MAINTAINED

少なくとも 現在のメジャーバージョンにおける次のマイナー・リリースまでは後方非互換に変更されない機能を意図しています。削除予定の場合は、まず DEPRECATED に降格されます。

STABLE

現在のメジャーバージョン( 5.* )では後方非互換に変更されない機能を意図しています。

@API アノテーションが型に存在していた場合、 その型の全てのパブリックなメンバーにも同様に適用されると考えられます。 メンバーは、それより低い安定性の異なる status 値を宣言することが許されています。

8.2. 実験的なAPI

次の表は、( @API(status = EXPERIMENTAL) を通して) 実験的な ものとして 現在のところ指定されているAPIを列挙しています。これらのAPIを使うときは注意してください。

パッケージ名 クラス名 タイプ

org.junit.jupiter.api

AssertionsKt

クラス

org.junit.jupiter.api

DynamicContainer

クラス

org.junit.jupiter.api

DynamicNode

クラス

org.junit.jupiter.api

DynamicTest

クラス

org.junit.jupiter.api

TestFactory

アノテーション

org.junit.jupiter.api.condition

DisabledIf

アノテーション

org.junit.jupiter.api.condition

EabledIf

アノテーション

org.junit.jupiter.api.extension

ScriptEvaluationException

クラス

org.junit.jupiter.params

ParameterizedTest

アノテーション

org.junit.jupiter.params.aggregator

AggregateWith

アノテーション

org.junit.jupiter.params.aggregator

ArgumentAccessException

クラス

org.junit.jupiter.params.aggregator

ArgumentsAccessor

インターフェイス

org.junit.jupiter.params.aggregator

ArgumentsAggregationException

クラス

org.junit.jupiter.params.aggregator

ArgumentsAggregator

インターフェイス

org.junit.jupiter.params.converter

ArgumentConversionException

クラス

org.junit.jupiter.params.converter

ArgumentConverter

インターフェイス

org.junit.jupiter.params.converter

ConvertWith

アノテーション

org.junit.jupiter.params.converter

JavaTimeConversionPattern

アノテーション

org.junit.jupiter.params.converter

SimpleArgumentConverter

クラス

org.junit.jupiter.params.provider

Arguments

インターフェイス

org.junit.jupiter.params.provider

ArgumentsProvider

インターフェイス

org.junit.jupiter.params.provider

ArgumentsSource

アノテーション

org.junit.jupiter.params.provider

ArgumentsSources

アノテーション

org.junit.jupiter.params.provider

CsvFileSource

アノテーション

org.junit.jupiter.params.provider

CsvSource

アノテーション

org.junit.jupiter.params.provider

EnumSource

アノテーション

org.junit.jupiter.params.provider

MethodSource

アノテーション

org.junit.jupiter.params.provider

ValueSource

アノテーション

org.junit.jupiter.params.support

AnnotationConsumer

インターフェイス

org.junit.platform.engine.discovery

ModuleSelector

クラス

8.3. @API ツールのサポート

@API Guardian プロジェクトは、 @API の付与された APIの開発者と利用者のためのツールサポートを提供することを計画しています。 例えば、ツールサポートは、JUnit APIが @API アノテーションの宣言に応じて 使われているかを確認する手段を提供する見込みです。

9. 貢献者

GitHub上の 現在の貢献者リスト を直接ご覧ください。

10. リリースノート

リリースノートは こちら で利用可能です。