1. 概要
この文書は、テストを書くプログラマーや拡張機能の作者、ビルドツールやIDEを含むテストエンジンの作者に対して、 包括的なリファレンスを提供することを目的としています。
|
Translations
|
1.1. JUnit 5とは
これまでのバージョンのJUnitとは異なり、JUnit 5は 3つのサブプロジェクトに含まれる複数のモジュールで構成されます。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform は、JVM上で テストフレームワークを起動する
ための基盤となり、このプラットフォーム上で動作するテストフレームワークを開発するための
TestEngine APIを定義しています。加えて、コマンドラインからプラットフォームを起動するための
Console Launcher や、
Gradle と Maven
用のビルドプラグイン、 JUnit 4ベースのテストランナー
などを提供し、あらゆる TestEngine を実行できるようになっています。
JUnit Jupiter は、JUnit 5でテストや拡張機能を書くための新しい
プログラミングモデル と 拡張モデル の組み合わせです。
Jupiterサブプロジェクトは、プラットフォーム上でJupiterベースのテストを実行するための
TestEngine を提供します。
JUnit Vintage は、プラットフォーム上でJUnit 3またはJUnit 4
ベースのテストを実行するための TestEngine を提供します。
1.2. サポートされるJavaバージョン
JUnit 5を実行するには、Java 8(またはそれ以上)が必要となります。 ただし、それより古いバージョンのJDKでコンパイルされたコードをテストすることは可能です。
1.3. 助けてもらうには
Stack Overflow で質問するか、 Gitter で私たちにチャットしてください。
2. インストール
最終リリースや各マイルストーンでの成果物は、Maven Centralリポジトリにデプロイされています。
スナップショットは、Sonatype スナップショットリポジトリ の /org/junit 以下にデプロイされています。
2.1. 依存関係メタデータ
2.1.1. JUnit Platform
-
Group ID:
org.junit.platform -
Version:
1.3.0 -
Artifact IDs:
junit-platform-commons-
JUnitの内部的な共通ライブラリ/ユーティリティです。これらのユーティリティは、 JUnitフレームワーク自身で使われることだけを想定しています。 外部のプログラムからの利用はサポートされていません。 利用は自己責任で!
junit-platform-console-
コンソールからJUnit Platform上でテストを見つけて実行する機能を提供します。 詳細は、 Console Launcher を参照してください。
junit-platform-console-standalone-
すべての依存関係を含む実行可能なJARは、Maven Centralリポジトリの junit-platform-console-standalone ディレクトリで提供されています。詳細は、 Console Launcher を参照してください。
junit-platform-engine-
テストエンジンの公開APIです。詳細は、 Plugging in your own Test Engine を参照してください。
junit-platform-launcher-
テストプランを構成して起動するための公開APIです。通常は、IDEやビルドツールに使われます。 詳細は、 JUnit Platform Launcher API を参照してください。
junit-platform-runner-
JUnit Platform上のテストおよびテストスイートをJUnit 4環境で実行するテストランナーです。 詳細は、 JUnit Platformの実行にJUnit 4を使う を参照してください。
junit-platform-suite-api-
JUnit Platform上でテストスイートを構成するためのアノテーションです。 JUnitPlatformランナー と(おそらく)サードパーティーの
TestEngine実装にもサポートされます。 junit-platform-surefire-provider-
Maven Surefire を使って、 JUnit Platform上でテストを見つけて実行する機能を提供します。
2.1.2. JUnit Jupiter
-
Group ID:
org.junit.jupiter -
Version:
5.3.0 -
Artifact IDs:
junit-jupiter-apijunit-jupiter-engine-
JUnit Jupiterのテストエンジン実装です。実行時だけ必要になります。
junit-jupiter-params-
JUnit Jupiterで パラメーター化テスト をサポートします。
junit-jupiter-migrationsupport-
JUnit 4からJUnit Jupiterへの移行をサポートします。 JUnit Jupiter上で、JUnit 4ルールを実行したいときだけ必要になります。
2.1.3. JUnit Vintage
-
Group ID:
org.junit.vintage -
Version:
5.3.0 -
Artifact ID:
junit-vintage-engine-
JUnit Vintageのテストエンジン実装です。JUnit 3またはJUnit 4で書かれた昔のテストを 新しいJUnit Platform上で実行できるようにします。
2.3. JUnit Jupiterサンプルプロジェクト
junit5-samples リポジトリは、
JUnit JupiterベースとJUnit Vintageベースのプロジェクトの様々なサンプルを提供します。
以下のプロジェクトでビルドスクリプト(例えば、build.gradle や pom.xml など)
を見つけられるでしょう。
-
GradleとJavaの場合は、
junit5-jupiter-starter-gradleプロジェクトを参照してください -
GradleとKotlinの場合は、
junit5-jupiter-starter-gradle-kotlinプロジェクトを参照してください -
GradleとGroovyの場合は、
junit5-jupiter-starter-gradle-groovyプロジェクトを参照してください -
Mavenの場合は、
junit5-jupiter-starter-mavenプロジェクトを参照してください -
Antの場合は、
junit5-jupiter-starter-antプロジェクトを参照してください
3. テストを書く
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 パッケージに含まれます。
| アノテーション | 説明 |
|---|---|
|
このメソッドが、テストメソッドであることを示します。JUnit 4の |
|
このメソッドが、パラメーター化テスト であることを示します。このメソッドは、オーバーライド されない限り、継承 されます。 |
|
このメソッドが、繰り返しテスト のためのテストテンプレートであることを示します。このメソッドは、オーバーライド されない限り、継承 されます。 |
|
このメソッドが、動的テスト のためのテストファクトリであることを示します。このメソッドは、オーバーライド されない限り、継承 されます。 |
|
アノテーションを付与したテストクラスに対して、テストインスタンスのライフサイクル を設定するために使われます。このアノテーションは、継承 されます。 |
|
このメソッドが、テストケースのテンプレート であることを示します。このメソッドは、登録した プロバイダ が返す実行コンテキストの数に応じて、複数回呼び出されます。このメソッドは、オーバーライド されない限り、継承 されます。 |
|
テストクラスやテストメソッドにカスタムの表示名を指定します。このアノテーションは、継承 されません。 |
|
このメソッドが、現在のクラスの |
|
このメソッドが、現在のクラスの |
|
このメソッドが、現在のクラスの すべての |
|
このメソッドが、現在のクラスの すべての |
|
このクラスが、ネストした非staticのテストクラスであることを示します。 |
|
クラスまたはメソッドレベルでテストをフィルタリングするための タグ を宣言するのに使われます。TestNGのテストグループやJUnit 4のCategoryと同じです。このアノテーションは、クラスレベルでは 継承 されますが、メソッドレベルでは 継承 されません。 |
|
テストクラスやテストメソッドを 無効化 するのに使われます。JUnit 4の |
|
カスタムの 拡張機能 を登録するために使われます。このアノテーションは、継承 されます。 |
@Test, @TestTemplate, @RepeatedTest, @BeforeAll, @AfterAll, @BeforeEach, @AfterEach
アノテーションの付与されたメソッドは、戻り値を返してはいけません。
| いくつかのアノテーションは現在 実験中 です。詳細は、 実験的なAPI のテーブルを参照してください。 |
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 method) とは、@Test, @RepeatedTest, @ParameterizedTest,
@TestFactory, @TestTemplate のいずれかが直接またはメタアノテーションとして付与された
インスタンスメソッドです。テストクラス (test class) とは、テストメソッドを少なくとも1つもつ
トップレベルまたはstaticな内部クラスです。
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("失敗するテスト");
}
@Test
@Disabled("デモ用")
void skippedTest() {
// 実行されない
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
}
テストクラスやテストメソッドは、 public である必要がありません。
|
3.3. 表示名
テストクラスやテストメソッドは、カスタムの表示名を宣言できます。 空白や特殊文字だけでなく絵文字も含めることができ、テストランナーやテストレポートに表示されます。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("特殊なテストケース")
class DisplayNameDemo {
@Test
@DisplayName("スペースを 含む カスタムの テスト名")
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, "省略可能なアサーションメッセージは最後のパラメーター");
assertTrue('a' < 'b', () -> "アサーションメッセージは遅延評価できる -- "
+ "不必要に複雑なメッセージを構築するコストを割けるために");
}
@Test
void groupedAssertions() {
// アサーションをグループ化すると、すべてのアサーションが一度に実行され、
// すべての失敗がまとめて報告される。
assertAll("person",
() -> assertEquals("John", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}
@Test
void dependentAssertions() {
// コードブロック内でアサーションが失敗すると、同じブロック内の後続のコードはスキップされる。
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);
// 上のアサーションが成功した場合のみ実行される。
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("n"))
);
},
() -> {
// グループ化されたアサーションは、first name のアサーションとは独立して実行される。
String lastName = person.getLastName();
assertNotNull(lastName);
// 上のアサーションが成功した場合のみ実行される。
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() {
// 次のアサーションは成功する。
assertTimeout(ofMinutes(2), () -> {
// 2分未満で終わるタスクを実行する。
});
}
@Test
void timeoutNotExceededWithResult() {
// 次のアサーションは成功し、指定されたオブジェクトを返す。
String actualResult = assertTimeout(ofMinutes(2), () -> {
return "a result";
});
assertEquals("a result", actualResult);
}
@Test
void timeoutNotExceededWithMethod() {
// 次のアサーションは、メソッド参照を実行してオブジェクトを返す。
String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
assertEquals("Hello, World!", actualGreeting);
}
@Test
void timeoutExceeded() {
// 次のアサーションは、以下のようなエラーメッセージを出して失敗する:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// 10ミリ秒より時間のかかるタスクをシミュレートする。
Thread.sleep(100);
});
}
@Test
void timeoutExceededWithPreemptiveTermination() {
// 次のアサーションは、以下のようなエラーメッセージを出して失敗する:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// 10ミリ秒より時間のかかるタスクをシミュレートする。
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が提供するアサーション機能だけで十分ですが、 時にはさらなるパワーや マッチャー のような追加機能が必要になることもあります。 そのような場合には、 AssertJ や Hamcrest、 Truth のようなサードパーティーの アサーションライブラリを使うことをJUnitチームはお勧めします。 開発者は、好みに応じてアサーションライブラリを自由に選択することができます。
例えば、 マッチャー と流れるようなAPIを組み合わせれば、アサーションをもっと説明的で
読みやすくすることができます。しかし、JUnit Jupiterの org.junit.jupiter.api.Assertions クラスは、
JUnit 4の org.junit.Assert クラスのようにHamcrestの
Matcher
を受け付ける
assertThat()
メソッドを提供していません。代わりに、開発者はサードパーティーのアサーションライブラリが
提供するマッチャーを利用することが推奨されます。
次の例は、JUnit JupiterテストでHamcrestの assertThat() を使う方法を示しています。
Hamcrestライブラリをクラスパスに追加しさえすれば、 assertThat() や is()、 equalTo()
のようなメソッドをstatic importして、下の 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")));
// 残りのテスト
}
@Test
void testOnlyOnDeveloperWorkstation() {
assumeTrue("DEV".equals(System.getenv("ENV")),
() -> "テストを中断: 開発者マシンではない");
// 残りのテスト
}
@Test
void testInAllEnvironments() {
assumingThat("CI".equals(System.getenv("ENV")),
() -> {
// これらのアサーションはCIサーバーでのみ実行する
assertEquals(2, 2);
});
// これらのアサーションはすべての環境で実行する
assertEquals("a string", "a string");
}
}
3.6. テストの無効化
テストクラス全体または個々のテストメソッドは、 @Disabled アノテーションや
条件付きテスト実行 で説明するアノテーション、
あるいはカスタムの 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 です
(テストの無効化 参照)。
JUnit Jupiterは、org.junit.jupiter.api.condition パッケージで @Disabled
以外にもアノテーションベースの条件をサポートしています。これにより、開発者は 宣言的に
コンテナやテストを有効化したり、無効化したりすることができます。
詳細は、以降の節を参照してください。
|
合成アノテーション
以降で説明するいずれの 条件 アノテーションも、カスタムの 合成アノテーション
を作るためのメタアノテーションとして使われうる点に注意してください。例えば、
@EnabledOnOsデモ の |
|
以降の節で取り上げられる 条件 アノテーションは、あるテストインターフェースやテストクラス、
テストメソッドに対して一度しか宣言できません。条件アノテーションがある要素に対して直接、
間接、あるいはメタアノテーションで複数回指定されたとしても、JUnitが最初に見つけた
アノテーションだけが使われます。その他のアノテーションは、黙って無視されます。ただし、
|
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 アノテーションを使うと、
指定した名前をもつ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
アノテーションを使うと、指定した名前をもつ環境変数の値に応じて、コンテナやテストを
有効にしたり無効にしたりできます。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 を使った条件付きテスト実行は、
現在 試験的な 機能です。詳細は、実験的なAPI
のテーブルを参考にしてください。
|
| もしスクリプトのロジックが現在のオペレーティングシステムやJavaランタイム環境の バージョン、特定のJVMシステムプロパティや環境変数だけに依存するなら、その目的専用の 組み込みアノテーションの使用を検討すべきです。詳細は、この章の前の節を参照してください。 |
| もし同じようなスクリプトベースの条件を何回も使っていると感じたら、 高速かつタイプセーフで、よりメンテナンスしやすい方法で実装するために、専用の ExecutionCondition 拡張を書くことを検討してください。 |
@Test // 静的なJavaScriptの式
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
// ...
}
@RepeatedTest(10) // 動的なJavaScriptの式
@DisabledIf("Math.random() < 0.214159")
void mightNotBeExecuted() {
// ...
}
@Test // システムプロパティをテストする正規表現
@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 // 複数行のスクリプト、カスタムのエンジン名、カスタムの理由(訳注:テストが無効化された場合に表示される)
@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) メソッドを介したマップ構造への
アクセスを提供します。
| Name | Type | Description |
|---|---|---|
|
accessor |
オペレーティングシステムの環境変数へのアクセッサ |
|
accessor |
JVMのシステムプロパティへのアクセッサ |
|
accessor |
設定パラメーターへのアクセッサ |
|
|
テストやコンテナの表示名 |
|
|
テストやコンテナにアサインされたすべてのタグ |
|
|
テストやコンテナの一意な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は テストメソッド (テストクラスとテストメソッド 参照) 毎にそのテストクラスの新しいインスタンスを生成して実行します。 この "per-method(メソッド毎)" のテストインスタンスライフサイクルは、JUnit Jupiter のデフォルトの挙動で、過去のJUnitのバージョンと同じです。
"per-method" のテストインスタンスライフサイクルモードが有効であったとしても、
条件 (例えば、@Disabled や
@DisabledOnOs など)によって テストメソッド が 無効化 されている場合は、
テストクラスのインスタンスが作り直されない点に注意してください。
|
もしすべてのテストメソッドを同じテストインスタンスで実行させたい場合は、テストクラスに
@TestInstance(Lifecycle.PER_CLASS) アノテーションを付与してください。
このモードを使うと、新しいテストインスタンスはテストクラス毎に1回だけ生成されます。
そのため、もしテストメソッドがインスタンス変数の状態に依存するのであれば、
@BeforeEach や @AfterEach メソッドで状態をリセットする必要があります。
"per-class(クラス毎)" モードは、デフォルトの "per-method(メソッド毎)" モードに比べて
追加のメリットがあります。具体的には、"per-class" モードでは @BeforeAll および
@AfterAll をインターフェースの default メソッドやクラスの非staticなメソッドに
対して宣言できるようになります。そのため、"per-class" モードでは @BeforeAll や
@AfterAll メソッドを @Nested テストクラスでも使えます。
もしプログラミング言語Kotlinでテストを書く場合は、テストインスタンスのライフサイクルを
"per-class" モードに切り替えると、@BeforeAll および @AfterAll メソッドを
より実装しやすいでしょう。
3.9.1. テストインスタンスのデフォルトのライフサイクルの変更
テストクラスやテストインターフェースに @TestInstance アノテーションを付与すると、
JUnit Jupiterは デフォルト のライフサイクルモードを使います。標準の デフォルト
モードは PER_METHOD ですが、テストプラン全体で デフォルト を変更することもできます。
テストインスタンスのデフォルトのライフサイクルモードを変更するには、単に
junit.jupiter.testinstance.lifecycle.default 設定パラメーター に
TestInstance.Lifecycle で定義された列挙定数を指定するだけです。
これは、JVMシステムプロパティや Launcher に渡される LauncherDiscoveryRequest
の 設定パラメーター 、あるいはJUnit Platform設定ファイルなどで指定できます
(詳細は、設定パラメーター 参照)。
例えば、デフォルトのライフサイクルモードを Lifecycle.PER_CLASS に設定するには、
次のシステムプロパティを指定してJVMを起動します。
-Djunit.jupiter.testinstance.lifecycle.default=per_class
ただし、デフォルトのライフサイクルモードをJUnit Platform設定ファイルで指定する方が、 よりロバストである点に注意してください。というのも、設定ファイルならプロジェクトの バージョン管理システムにチェックインでき、IDEやビルドツールからも使えるからです。
JUnit Platform設定ファイルを使ってデフォルトのライフサイクルモードを Lifecycle.PER_CLASS
にするには、クラスパスのルート(例えば、src/test/resources)に junit-platform.properties
という名前で次のファイルを作ります。
junit.jupiter.testinstance.lifecycle.default = per_class
| テストインスタンスの デフォルト のライフサイクルモードを変更する場合は、 一貫性のある方法で適用しないと予測できない結果や不安定なビルドにつながる可能性があります。 例えば、ビルドでは "per-class" をデフォルトとして設定していても、IDEが "per-method" でテストを実行していると、ビルドサーバーで起きたエラーをデバッグするのが難しくなります。 そのため、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());
}
}
}
}
非staticなネストクラス(すなわち、内部クラス)だけが、@Nested
テストクラスとして使えます。ネストの深さは自由で、すべての内部クラスがテストクラスの
一部とみなされます。ただし、1つだけ例外があり、@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 によって解決されます。
現状、自動的に登録される組み込みのResolverが3つあります。
-
TestInfoParameterResolver: もしメソッドの引数の型がTestInfoの場合、TestInfoParameterResolverが現在のテストに応じたTestInfoをその引数の値として 与えます。TestInfoは、テストの表示名やテストクラス、テストメソッド、付与されたタグなど、 現在のテストに関する情報を取得するために使用できます。表示名は、テストクラスやテストメソッド の名前か、@DisplayNameで設定されたカスタムの名前か、どちらかになります。TestInfoは、JUnit 4のTestNameルールの代替として使えます。 次の例は、TestInfoをテストのコンストラクタ、@BeforeEachメソッド、および@Testメソッドに注入する方法を示しています。
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の場合、RepetitionInfoParameterResolverがRepetitionInfoインスタンスを与えます。RepetitionInfoは、@RepeatedTestの現在の繰り返し回数や合計回数の情報を 取得するために使用できます。ただし、RepetitionInfoParameterResolverは@RepeatedTest以外では登録されないことに注意してください。 繰り返しテストの例 を参照。 -
TestReporterParameterResolver: もしメソッドの引数の型がTestReporterの場合、TestReporterParameterResolverがTestReporterインスタンスを与えます。TestReporterは、現在のテスト実行に関する追加のデータを出力するために使用できます。 このデータは、TestExecutionListener.reportingEntryPublished()を通して取得できるので、IDEで参照したり、レポートに含めたりできます。JUnit Jupiterでは、JUnit 4で
stdoutやstderrに情報を出力していた代わりに、TestReporterを使うべきです。@RunWith(JUnitPlatform.class)を使うときも、 すべてのレポート情報がstdoutに出力されます。
class TestReporterDemo {
@Test
void reportSingleValue(TestReporter testReporter) {
testReporter.publishEntry("a status message");
}
@Test
void reportKeyValuePair(TestReporter testReporter) {
testReporter.publishEntry("a key", "a value");
}
@Test
void reportMultipleKeyValuePairs(TestReporter testReporter) {
testReporter.publishEntry(
Map.of(
"user name", "dk38",
"award year", "1974"
));
}
}
そのほかの ParameterResolver は、@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);
}
}
実世界のユースケースとしては、MockitoExtension や SpringExtension
のソースコードを確認してください。
3.12. テストインターフェースとデフォルトメソッド
JUnit Jupiterでは、@Test, @RepeatedTest, @ParameterizedTest, @TestFactory,
@TestTemplate, @BeforeEach, @AfterEach アノテーションをインターフェースの
default メソッドに対して宣言することもできます。 @BeforeAll と @AfterAll
はテストインターフェースの 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))
);
}
}
@ExtendWith と @Tag はテストインターフェースに宣言することで、
そのインタフェースを実装したクラスにタグと拡張機能を自動的に継承させることができます。
テスト実行前後のコールバック にある
TimingExtension
のソースコードを参照してください。
@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 returnsPositiveNumberWhenComparedToSmallerValue() {
T value = createValue();
T smallerValue = createSmallerValue();
assertTrue(value.compareTo(smallerValue) > 0);
}
@Test
default void returnsNegativeNumberWhenComparedToLargerValue() {
T value = createValue();
T smallerValue = createSmallerValue();
assertTrue(smallerValue.compareTo(value) < 0);
}
}
テストクラスでこれらの契約インターフェースを実装することで、対応するテストケースを 継承することができます。もちろん、抽象メソッドは自分で実装する必要があります。
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 アノテーションを使って指定した回数だけ
繰り返しテストを実行する機能を提供しています。
繰り返しテストの1回毎の呼び出しは、ライフサイクルコールバックや拡張機能など、通常の
@Test メソッドの実行と同じように振る舞います。
次の例は、自動的に10回繰り返す repeatedTest() を宣言する方法を示しています。
@RepeatedTest(10)
void repeatedTest() {
// ...
}
繰り返し回数を指定するだけでなく、@RepeatedTest アノテーションの name
属性を使って、繰り返し実行毎の表示名を変更することもできます。表示名は、
静的なテキストと動的なプレースホルダーを組み合わせたパターンにできます。
以下が、現在サポートされているプレースホルダーです。
-
{displayName}:@RepeatedTestメソッドの表示名 -
{currentRepetition}: 現在の実行回数 -
{totalRepetitions}: トータルの実行回数
デフォルトの表示名は、"repetition {currentRepetition} of {totalRepetitions}"
というパターンに従います。そのため、前述の repeatedTest() に対する表示名は、
repetition 1 of 10, repetition 2 of 10 のようになります。
もし @RepeatedTest メソッドの名前を含めたい場合は、カスタムのパターンを指定するか、
定義済みの RepeatedTest.LONG_DISPLAY_NAME パターンを指定できます。
後者は、"{displayName} :: repetition {currentRepetition} of {totalRepetitions}"
というパターンと同等で、repeatedTest() :: repetition 1 of 10,
repeatedTest() :: repetition 2 of 10 のようになります。
現在の繰り返し回数やトータルの繰り返し回数をプログラムから取得するためには、
RepetitionInfo インスタンスを @RepeatedTest, @BeforeEach, @AfterEach
メソッドのいずれかに注入させることができます。
3.13.1. 繰り返しテストの例
この節の最後の RepeatedTestsDemo クラスは、繰り返しテストの様々な例を示しています。
repeatedTest() メソッドは、前の節で紹介した例と同じです。一方、
repeatedTestWithRepetitionInfo() は、トータルの繰り返し回数を取得するために
RepetitionInfo インスタンスをテストに注入させる方法を示しています。
その次の2つのメソッドは、@RepeatedTest メソッドに対するカスタムの @DisplayName
を各繰り返しの表示名に含める方法を示しています。customDisplayName() は、
カスタムの表示名とパターンを組み合わせて指定し、生成された表示名を検証するのに
TestInfo を使っています。
{displayName} が @DisplayName の宣言から Repeat! となり、
{currentRepetition}/{totalRepetitions} が 1/1 となります。
一方、customDisplayNameWithLongPattern() は前述の定義済みパターン
RepeatedTest.LONG_DISPLAY_NAME を使っています。
repeatedTestInGerman() は、繰り返しテストの表示名を外国語(この場合、ドイツ語)
に翻訳する方法を示しています。各繰り返しの表示名は、Wiederholung 1 von 5,
Wiederholung 2 von 5 のようになります。
beforeEach() メソッドは @BeforeEach アノテーションが付与されているため、
繰り返しテストの個々の繰り返し実行の度に実行されます。TestInfo と
RepetitionInfo をメソッドに注入させることで、現在実行中の繰り返しテストに
関する情報を取得できることが分かります。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() {
// ...
}
}
Unicodeテーマを有効にした ConsoleLauncher で 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. パラメーター化テスト
|
訳注:計算機科学の用語に従うのであれば、 argument を 実引数(メソッドを呼び出す側から見た引数)、 parameter を 仮引数(メソッドを呼ばれる側から見た引数) と区別して訳すべきだが、パラメーター化テストの説明では前者の argument を パラメーター と訳してしまった方が 直感的にイメージしやすいと訳者は考える。 原文でも両者の区別があいまいな箇所があるため、以降では 訳者の解釈に従って意訳している。気になる場合は、原文をあたること。 |
パラメーター化テストは、異なる実引数でテストを複数回実行できるようにします。
通常の @Test メソッドに似ていますが、@ParameterizedTest アノテーションを
使って宣言します。各呼び出しに対するパラメーターを生成するための ソース (source)
を少なくとも1つは宣言し、そのパラメーターはテストメソッドで 消費 (consume)
する必要があります。
以下は、パラメーターのソースとして String 配列を指定するために @ValueSource
アノテーションを使用した例を示しています。
@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 ✔
| パラーメーター化テストは、現在 実験中 の機能です。 詳細は、実験的なAPI の表を確認してください。 |
3.14.1. 必要なセットアップ
パラメーター化テストを使うには、junit-jupiter-params への依存を追加する必要があります。
詳細は、依存関係メタデータ を参照してください。
3.14.2. パラメーターの消費
パラメーター化テストのメソッドは、通常は設定されたソース
(パラメーターのソース 参照)
からパラメーターのインデックスとメソッドの仮引数のインデックスが1対1となるように
パラメーターを 消費 します
(@CsvSource の例を参照)。
しかし、ソースのすべてのパラメーターを単一のオブジェクトに 集約
してメソッドに渡すこともできます
(パラメーターの集約 参照)。
また、ParameterResolver を使って追加の引数を渡すこともできます
(例えば、TestInfo や TestReporter のインスタンスなど)。
パラメーター化テストメソッドは、次のルールに従って仮引数を宣言しなければなりません。
-
最初に、インデックス付き引数 (indexed argument) を0個以上宣言します
-
その次に、アグリゲーター (aggregator) を0個以上宣言します
-
最後に、
ParameterResolverによって渡される引数を0個以上宣言します
ここで、インデックス付き引数 (indexed argument) は、ArgumentsProvider
が生成した Arguments 中の指定されたインデックスに対するパラメーターです。
このパラメーターは、パラメーター化テストメソッドの仮引数リストで
同じインデックスに実引数として渡されます。
アグリゲーター (aggregator) は、ArgumentsAccessor 型または @AggregateWith
アノテーションが付与された仮引数です。
3.14.3. パラメーターのソース
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 メソッドは、値 1, 2, 3 をパラメーターとして
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 属性も提供しています。例えば、一部の名前をもつ列挙定数だけ除外したり、
正規表現で列挙定数を指定したりすることができます。
@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 は、テストクラスまたは外部のクラスの ファクトリ
メソッドによるパラメーターの指定を可能にします。
テストクラス内のファクトリメソッドは、@TestInstance(Lifecycle.PER_CLASS)
アノテーションが付与されていない限り、static でなければいけません。
一方、外部クラスのファクトリメソッドは、常に static でなければいけません。
これらのファクトリメソッドは、引数を受け取ることもできません。
ファクトリメソッドは、 @ParameterizedTest メソッドの各呼び出しに対して
実際の引数として渡される パラメーター の ストリーム を生成しなければなりません。
一般的に言えば、これは Arguments の Stream (つまり、Stream<Arguments>)
となりますが、実際の戻り値は様々な型をとりえます。ここでいう "ストリーム" とは、
JUnitが確実に Stream へ変換できるものであれば何でも構いません。例えば、
Stream, DoubleStream, LongStream, IntStream, Collection,
Iterator, Iterable, オブジェクト配列, プリミティブ型の配列などです。
ストリームの "パラメーター" は、 Arguments のインスタンス、オブジェクト配列
(つまり、Object[])、あるいはパラメーター化テストメソッドが1つだけしか
パラメーターを受け取らない場合には単一の値となります。
パラメーターが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");
}
プリミティブ型のストリーム (DoubleStream, IntStream, LongStream)
もサポートされています。
@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(int argument) {
assertNotEquals(9, argument);
}
static IntStream range() {
return IntStream.range(0, 20).skip(10);
}
パラメーター化テストメソッドが複数の仮引数を宣言している場合、以下で示すように Arguments
インスタンスかオブジェクト配列を要素とするコレクション、ストリーム、配列のいずれかを返す必要があります
(サポートされる戻り値型のさらなる詳細は、@MethodSource のJavadocを参照)。
arguments(Object…) は Arguments インターフェースで定義されるstaticファクトリメソッドである
点に注意してください。また、Arguments.of(Object…) を arguments(Object…)
の代わりに使用することもできます。
@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("foo", 1, Arrays.asList("a", "b")),
arguments("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#tinyStrings")
void testWithExternalMethodSource(String tinyString) {
// test with tiny string
}
}
class StringsProviders {
static Stream<String> tinyStrings() {
return Stream.of(".", "oo", "OOO");
}
}
@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 が発生します。
| Example Input | Resulting Argument List |
|---|---|
|
|
|
|
|
|
|
|
@CsvFileSource
@CsvFileSource は、クラスパスにあるCSVファイルを使えるようにします。
CSVファイルの各行は、パラメーター化テストの1回の実行になります。
@ParameterizedTest
@CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)
void testWithCsvFileSource(String first, int second) {
assertNotNull(first);
assertNotEquals(0, second);
}
Country, reference
Sweden, 1
Poland, 2
"United States of America", 3
@CsvSource のシンタックスとは異なり、@CsvFileSource はダブルクォート "
を引用符として使います。上の例の、"United States of America" を参考にしてください。
引用符で囲まれた空の値 "" は空の String になりますが、
(引用符で囲まれない)完全に 空 の値は null と解釈されます。
null の代入先の型がプリミティブ型の場合は、
ArgumentConversionException が発生します。
|
@ArgumentsSource
@ArgumentsSource は、カスタムの再利用可能な 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.14.4. パラメーターの変換
拡大変換
JUnit Jupiterは、@ParameterizedTest に与えられたパラメーターに対して、
プリミティブ型の
拡大変換 をサポートしています。例えば、@ValueSource(ints = { 1, 2, 3 })
アノテーションが付与されたパラメーター化テストでは、テストメソッドの仮引数を
int 型だけでなく long や float、double 型でも宣言することができます。
暗黙的な変換
@CsvSource のようなユースケースをサポートするために、JUnit Jupiter
は多くの組み込みの型変換を提供しています。
変換処理は、メソッドの仮引数の型に依存します。
例えば、@ParameterizedTest が TimeUnit 型の仮引数を宣言し、
ソースから実際に渡される型が String の場合、その文字列は対応する
TimeUnit 列挙定数に自動的に変換されます。
@ParameterizedTest
@ValueSource(strings = "SECONDS")
void testWithImplicitArgumentConversion(TimeUnit argument) {
assertNotNull(argument.name());
}
String インスタンスは、今のところ次のようなターゲット型に暗黙的に変換されます。
| Target Type | Example |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String → Object変換のフォールバック
上の表で示した文字列からターゲット型への暗黙的な変換に加えて、JUnit Jupiter
は String 型からターゲット型に自動変換するフォールバックの仕組みも提供しています。
この変換は、ターゲット型が以下に示す ファクトリメソッド か ファクトリコンストラクタ
を1つだけ宣言している場合に機能します。
-
ファクトリメソッド: ターゲット型に宣言された非privateの
staticメソッドで、String型の引数を1つだけ受け取ってターゲット型のインスタンスを返す。 メソッドの名前は任意であり、特定のルールに従う必要はない。 -
ファクトリコンストラクタ: ターゲット型に宣言された非privateのコンストラクタで、
String型の引数を1つだけ受け取ってインスタンスを作る。
| 複数の ファクトリメソッド が見つかった場合、それらは無視されます。 ファクトリメソッド と ファクトリコンストラクタ が両方見つかった場合は、 ファクトリメソッドの方が使われます。 |
例えば、次の @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
の1つだけしか提供していません。これは、合成アノテーション JavaTimeConversionPattern
経由で使用できます。
@ParameterizedTest
@ValueSource(strings = { "01.01.2017", "31.12.2017" })
void testWithExplicitJavaTimeConverter(
@JavaTimeConversionPattern("dd.MM.yyyy") LocalDate argument) {
assertEquals(2017, argument.getYear());
}
3.14.5. パラメーターの集約
デフォルトでは、@ParameterizedTest のメソッドに与えられる各パラメーター
は、1つの仮引数に対応します。結果として、ソースが多数のパラメーターを生成する場合、
メソッドシグネチャが大きく(仮引数が多く)なります。
そのような場合には、複数の仮引数を定義する代わりに ArgumentsAccessor
を使うことができます。このAPIを使うと、テストメソッドに渡した単一の
ArgumentsAccessor 経由ですべてのパラメーターにアクセスすることができます。
加えて、
暗黙的な変換
で説明した型変換もサポートされます。
@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はカスタムの再利用可能な
アグリゲーター (aggregator) もサポートしています。
カスタムアグリゲーターを使うには、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.14.6. 表示名のカスタマイズ
デフォルトでは、パラメーター化テストの表示名には、呼び出しインデックスとすべてのパラメーターの
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 ✔
次のプレースホルダーがサポートされています。
| Placeholder | Description |
|---|---|
|
現在の呼び出しインデックス(1始まり) |
|
カンマ区切りの完全なパラメーターのリスト |
|
個々のパラメーター |
3.14.7. ライフサイクルと相互運用性
パラメーター化テストの各呼び出しは、通常の @Test メソッドと同じライフサイクルをもちます。
例えば、@BeforeEach メソッドが各呼び出しの前に実行されます。
動的テスト と同様に、パラメーター化テストの各呼び出しは、
IDEのテストツリーの中に1つずつ表示されます。同じテストクラスの中で、
@Test メソッドと @ParameterizedTest メソッドを混在させるのも自由です。
ParameterResolver 拡張を @ParameterizedTest メソッドと使いたいかもしれません。
その場合は、パラメーターのソースから渡される引数を先に宣言する必要があります。
テストクラスは、パラメーター化テストだけでなく通常のテストを含んでいる可能性もあるので、
パラメーターをライフサイクルメソッド (例えば、@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.15. テストテンプレート
@TestTemplate メソッドは、通常のテストケースではなく、テストケースのテンプレートです。
登録したプロバイダが返す実行コンテキストの数に応じて、複数回呼び出されるよう設計されています。
そのため、テストテンプレートは TestTemplateInvocationContextProvider
と一緒に使う必要があります。
テストテンプレートメソッドの各呼び出しは、ライフサイクルコールバックや拡張機能など、
通常の @Test メソッドの実行と同じように振る舞います。使用例については、
テストテンプレートに実行コンテキストを与える を参照してください。
3.16. 動的テスト
アノテーション で説明したJUnit Jupiterの標準の @Test
アノテーションは、JUnit 4の @Test アノテーションととてもよく似ています。
どちらもテストケースを実装するメソッドを示します。これらのテストケースは、
完全にコンパイル時に指定され、その振る舞いが実行時には変えられないという意味で、
静的なものです。 前提条件 (Assumption) の機能は、動的な振る舞いの基本を
提供しますが、表現力は意図的に限定されています。
JUnit Jupiterでは、これらの標準的なテストに加えて、完全に新しいプログラミング
モデルが導入されました。それは、動的テスト です。動的テストは、
@TestFactory アノテーションを付与したファクトリメソッドによって、
実行時に生成されます。
@Test メソッドとは異なり、@TestFactory メソッド自身はテストケースではなく、
テストケースのファクトリとなります。ファクトリの出力が動的テストになります。
技術的に言うと、@TestFactory メソッドは、DynamicNode インスタンスの
Stream, Collection, Iterable, Iterator, 配列のいずれかを
返さなければなりません。DynamicNode のインスタンス化可能なサブクラスには、
DynamicContainer と DynamicTest があります。DynamicContainer
インスタンスは、表示名 と子どもの DynamicNode リストで構成され、
入れ子になった DynamicNode の階層を作成できるようになっています。
DynamicTest インスタンスが遅延実行されることで、動的かつ非決定的な
テストケースの生成を実現します。
@TestFactory が返した Stream は、stream.close() 呼び出しによって
適切にクローズされます。これにより、Files.lines() などのリソースを
安全に使うことができます。
@Test メソッドと同じように、@TestFactory メソッドも private または
static であってはいけません。また、ParameterResolvers で解決される
パラメーターを宣言することもできます。
DynamicTest は、実行時に生成されるテストケースで、表示名 と Executable
で構成されます。Executable は、@FunctionalInterface であり、
動的テストの実装が ラムダ式 または メソッド参照 で与えられることを意味します。
|
動的テストのライフサイクル
動的テストの実行ライフサイクルは、通常の @Test とはかなり違います。
特に、個々の動的テストに対しては、ライフサイクルコールバックはありません。
これは、@BeforeEach や @AfterEach メソッドとその拡張コールバックが、
@TestFactory メソッドに対して実行され、個々の 動的テスト
に対しては実行されないことを意味しています。言い換えれば、動的テストのラムダ式から
テストインスタンスのフィールドにアクセスしていたとしても、同じ @TestFactory
メソッドから生成された個々の動的テストを実行する間は、コールバックメソッドにも
拡張機能にもそのフィールドはリセットされないのです。
|
JUnit Jupiter 5.3.0 の時点では、動的テストは常にファクトリメソッドで 生成しなければなりません。ただし、将来のリリースで、登録の仕組みが追加されるかもしれません。
| 動的テストは現在 実験中 の機能です。詳細は、 実験的なAPI の表を参照してください。 |
3.16.1. 動的テストの例
次の DynamicTestsDemo クラスは、テストファクトリと動的テストの例を示しています。
最初のメソッドは、不正な戻り値型を返しています。不正な戻り値型はコンパイル時には
検出できないため、実行時に検出されて JUnitException がスローされます。
その次の5つのメソッドは、DynamicTest インスタンスの Collection, Iterable,
Iterator, Stream を生成するとてもシンプルな例です。ほとんどの例は、
動的な振る舞いではなく、単にサポートされている戻り値の型をデモしています。
しかし、dynamicTestsFromStream() と dynamicTestsFromIntStream() は、
与えられた文字列リストや数値の範囲に対して簡単に動的なテストを生成できることを
示しています。
その次のメソッドは、真に動的な性質のものです。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
DynamicTest[] dynamicTestsFromArray() {
return new DynamicTest[] {
dynamicTest("7th dynamic test", () -> assertTrue(true)),
dynamicTest("8th dynamic test", () -> assertEquals(4, 2 * 2))
};
}
@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<>() {
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()))
))
)));
}
}
3.17. 並列実行
デフォルトでは、JUnit Jupiterのテストはシングルスレッドで逐次実行されます。バージョン
5.3からは、オプトインの機能として(例えば高速化のために)テストの並列実行を利用できます。
並列実行を有効にするには、単に junit-platform.properties ファイルで
設定パラメーター junit.jupiter.execution.parallel.enabled に true
を指定するだけです(その他の選択肢については、設定パラメーター を参照)。
有効にすると、JUnit Jupiterエンジンは与えられた 設定 と宣言的に指定された 同期化 に従って、 テストツリーのすべてのレベルのテストを並列実行します。なお、 標準出力/標準エラー出力のキャプチャ 機能は別途有効にする必要がある点に注意してください。
| テストの並列実行は、今のところ 実験的な 機能です。JUnitチームがこの機能を改善し、 最終的には 昇格 できるよう、試しに使ってみてフィードバックをください。 |
3.17.1. 設定
希望する並列数やプールの最大サイズなどのプロパティは、
ParallelExecutionConfigurationStrategy を使って設定できます。
JUnit Platformは、すぐに使える2つの実装(dynamic と fixed)を提供しています。
なお、代わりに独自の custom 戦略を実装することもできます。
並列戦略を選択するには、設定パラメーター junit.jupiter.execution.parallel.config.strategy
に次のいずれかを指定します。
dynamic-
利用可能なプロセッサ/コア数に、設定パラメーター
junit.jupiter.execution.parallel.config.dynamic.factorの値 (デフォルトは1)を掛けた数に基づいて並列数を計算します。 fixed-
設定パラメーター
junit.jupiter.execution.parallel.config.fixed.parallelismの値を並列数として使用します。 custom-
設定パラメーター
junit.jupiter.execution.parallel.config.custom.classで指定された独自のParallelExecutionConfigurationStrategy実装を使って、 並列数を決定します。
もし並列戦略が指定されていなかった場合、JUnit Jupiterは dynamic 戦略を使います。
つまり、並列数は利用可能なプロセッサ/コア数の数と同じになります。
3.17.2. 同期化
JUnit Jupiterは、異なるテスト間で共有リソースを使うときの実行モデルを変更し、
同期化を可能にするためのアノテーションベースの仕組みを2つ
org.junit.jupiter.api.parallel パッケージで提供しています。
並列実行が有効な場合、デフォルトではすべてのクラスとメソッドが同時に実行されます。
@Execution アノテーションを使うことで、アノテーションが付与された要素と
(もしあれば)サブ要素の実行モデルを変更することができます。
次の2つのモードが利用可能です。
SAME_THREAD-
親と同じスレッドで実行することを強制します。例えば、テストメソッドに指定した場合、 該当メソッドはテストクラスの
@BeforeAllメソッドや@AfterAllメソッドを 実行するスレッドと同じスレッドで実行されます。 CONCURRENT-
リソース制約で同一スレッドでの実行が強制されない限り、別スレッドで並列実行されます。
加えて、@ResourceLock アノテーションを使うと、テストクラスやテストメソッドが
同期化されたアクセスを必要とする共有リソースを使用していると宣言できます。
仮に次のサンプルのテストが並列実行されたとすると、結果は不安定 (flaky) 、
つまり成功したり失敗したりします。
というのも、同じシステムプロパティを読み書きする競合状態があるからです
(訳注: @ResourceLock による同期化がない仮定の話)。
@Execution(CONCURRENT)
class SharedResourcesDemo {
private Properties backup;
@BeforeEach
void backup() {
backup = new Properties();
backup.putAll(System.getProperties());
}
@AfterEach
void restore() {
System.setProperties(backup);
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)
void customPropertyIsNotSetByDefault() {
assertNull(System.getProperty("my.prop"));
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
void canSetCustomPropertyToFoo() {
System.setProperty("my.prop", "foo");
assertEquals("foo", System.getProperty("my.prop"));
}
@Test
@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)
void canSetCustomPropertyToBar() {
System.setProperty("my.prop", "bar");
assertEquals("bar", System.getProperty("my.prop"));
}
}
共有リソースへのアクセスが @ResourceLock アノテーションで宣言されている場合、
JUnit Jupiterエンジンはこの情報を活用し、互いにコンフリクトするテストが同時に
実行されないよう保証します。
使用されるリソースを一意に特定する文字列に加えて、アクセスモードを指定することもできます。
同じリソースの READ アクセスを要求する2つのテストは同時に実行することができますが、
READ_WRITE アクセスを要求するテストが実行中の場合は実行できません。
4. テストを実行する
4.1. IDEのサポート
4.1.1. IntelliJ IDEA
IntelliJ IDEAは、バージョン2016.2からJUnit Platform上でのテスト実行をサポートしています。
詳細は、
IntelliJ IDEAブログの投稿
を参照してください。ただし、実際にはIntelliJ IDEA 2017.3またはそれより新しいバージョンの
利用が推奨される点に注意してください。というのも、プロジェクトで利用されるAPIバージョンに応じて
junit-platform-launcher, junit-jupiter-engine, junit-vintage-engine
のJARを自動的にダウンロードしてくれるからです。
| IntelliJ IDEAのバージョン2017.3より前のリリースは、JUnit 5 の特定のバージョンを同梱している点に注意してください。もしもJUnit Jupiter の新しいバージョンを使いたい場合、IDE内でのテスト実行はバージョンのコンフリクトで 失敗する可能性があります。そのような場合は、IntelliJ IDEAに同梱されている JUnit 5より新しいバージョンを使うために、以下の手順に従ってください。 |
JUnit 5の異なるバージョン (例えば、 5.3.0) を使うには、
junit-platform-launcher, junit-jupiter-engine, junit-vintage-engine
の対応するバージョンのJARをクラスパスに追加する必要があります。
// Only needed to run tests in a version of IntelliJ IDEA that bundles older versions
testRuntime("org.junit.platform:junit-platform-launcher:1.3.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.3.0")
testRuntime("org.junit.vintage:junit-vintage-engine:5.3.0")
<!-- 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.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
4.1.2. Eclipse
Eclipse IDEは、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
本書執筆時点では、IntelliJ IDEAとEclipse以外のIDEは、JUnit Platform 上でのテスト実行を直接はサポートしていません。しかし、JUnitチームは あなたが使っているIDEでJUnit 5を試せるように、2つの中間解を提供しています。 Console Launcher を手動で使うか、 JUnit 4ベースのランナー でテストを実行することができます。
4.2. ビルドサポート
4.2.1. Gradle
Gradleは バージョン4.6 から、
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チームによって開発されていた |
設定パラメーター
Gradle標準の 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ベースのテストをサポートするためには、JUnit Jupiter API
に対する testCompile 依存と、JUnit Jupiter TestEngine
実装に対する testRuntime 依存を次のように指定する必要があります。
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-api:5.3.0")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.3.0")
}
JUnit Platformは、JUnit 4ベースのテストも実行することができます。
そのためには、JUnit 4に対する testCompile 依存と、JUnit Vintage
TestEngine 実装に対する testRuntime 依存が必要です。
dependencies {
testCompile("junit:junit:4.12")
testRuntime("org.junit.vintage:junit-vintage-engine:5.3.0")
}
ロギングを設定する(省略可)
JUnitは、java.util.logging パッケージ(別名、JUL)の Java Logging API
を警告やデバッグ情報の出力に利用しています。設定オプションについては、
LogManager の公式ドキュメントを参照してください。
ログを Log4j や Logback のような他のロギングフレームワークにリダイレクトする
ことも可能です。LogManager のカスタム実装を提供するロギングフレームワークを
使う場合は、java.util.logging.manager システムプロパティに LogManager
実装クラスの 完全修飾名 を指定してください。以下は、Log4j 2.x
(詳細は、 Log4j JDK Logging Adapter 参照)を設定する例です。
test {
systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
}
その他のロギングフレームワークは、java.util.logging のログをリダイレクトする
方法が異なります。例えば Logback では、
JULからSLF4Jへのブリッジ
を実行時のクラスパスに依存関係として追加して使います。
4.2.2. Maven
JUnitチームによって開発されていた junit-platform-surefire-provider
は非推奨となり、JUnit Platform 1.4で削除が予定されています。
代わりに、Maven Surefireを使用してください。
|
バージョン 2.22.0 から、
Maven SurefireはJUnit Platform上でテストを実行するための
ネイティブサポート
を提供しています。 junit5-jupiter-starter-maven プロジェクトの pom.xml
ファイルがその使い方を示すとともに、Mavenビルドを設定するための出発点となるでしょう。
テストエンジンを設定する
Maven Surefireにテストを実行させるためには、TestEngine の実装が少なくとも1つ
実行時のクラスパスに必要です。
JUnit Jupiterベースのテストをサポートするためには、JUnit Jupiter APIと
JUnit Jupiter TestEngine 実装に対する test スコープの依存を
次のように指定する必要があります。
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
...
<dependencies>
...
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
...
</dependencies>
...
Maven Surefireは、JUnit 4ベースのテストも実行することができます。
そのためには、次のようにJUnit 4とJUnit Vintage TestEngine 実装に対する test
スコープの依存が必要です。
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
</plugin>
</plugins>
</build>
...
<dependencies>
...
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
...
</dependencies>
...
テストクラス名でフィルタリングする
Maven Surefireプラグインは、完全修飾名が次のパターンにマッチするテストクラスを探します。
-
**/Test*.java -
**/*Test.java -
**/*Tests.java -
**/*TestCase.java
なお、すべてのネストクラス(staticな内部クラスも含む)はデフォルトで除外されます。
ただし、pom.xml ファイルの include および exclude
ルールを明示的に設定することで、デフォルトの動作を変更することもできます。
例えば、Maven Surefireがstaticな内部クラスを除外しないようにするなどです。
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<excludes>
<exclude/>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
...
詳細は、 Maven Surefireのドキュメント Inclusions and Exclusions of Tests を参照してください。
タグでフィルタリングする
次のように、タグまたは タグ式 を使ってテストをフィルタリングすることもできます。
-
タグ または タグ式 をテスト対象に含めるには、
groupsを指定します。 -
タグ または タグ式 をテスト対象から除外するには、
excludedGroupsを指定します。
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<groups>acceptance | !feature-a</groups>
<excludedGroups>integration, regression</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
...
設定パラメーター
configurationParameters プロパティにJavaの Properties ファイル形式の
キー・値のペアを与えるか、junit-platform.properties ファイルを与えることで、
テストの発見・実行に影響を与えるJUnit Platformの
設定パラメーター を指定できます。
...
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.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>
</plugin>
</plugins>
</build>
...
4.2.3. Ant
Ant のバージョン 1.10.3 から、
JUnit Platform上でのテスト実行をネイティブサポートするために
junitlauncher
タスクが導入されました。junitlauncher タスクは、単にJUnit Platform
を起動して、選択したテストを渡すだけの責務をもちます。その後、JUnit Platform
が登録されたテストエンジンにテストの発見・実行を移譲します。
junitlauncher タスクは、
ユーザーがテストエンジンに実行させたいテストを選択するための
リソース集合
のように、Antの既存機能と可能な限り一貫性を保とうとしています。
これにより、Antの他のコアタスクと比べても、一貫性があり直感的なものになっています。
Ant 1.10.3 でリリースされた junitlauncher タスクは、
JUnit Platformを起動する基本的かつ最低限のサポートだけを提供します。
追加の機能強化 (異なるJVMにforkしてのテスト実行サポートを含む) は、
今後のAntリリースで利用可能になる予定です。
|
junit5-jupiter-starter-ant プロジェクトの build.xml
ファイルが使い方を示しています。
基本的な使い方
次の例は、単一のテストクラス (org.myapp.test.MyFirstJUnit5Test)
を選択するように junitlauncher タスクを設定する方法を示します。
<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 要素で実行したい単一のテストクラスを指定することができます。
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 要素を使うと、異なる場所にある複数のテストクラスを選択できます。
使い方と設定オプションに関する詳細は、Ant公式ドキュメントの
junitlauncher タスク
を参照してください。
4.3. Console Launcher
ConsoleLauncher は、コンソールからJUnit Platformを起動するための
Javaのコマンドラインアプリケーションです。例えば、JUnit VintageとJUnit Jupiter
のテストを実行し、結果をコンソールに表示するのに使えます。
すべての依存関係を含んだ実行可能な junit-platform-console-standalone-1.3.0.jar
がMavenセントラルリポジトリの
junit-platform-console-standalone
ディレクトリで公開されています。スタンドアロン版の ConsoleLauncher は、次のように
実行する ことができます。
java -jar junit-platform-console-standalone-1.3.0.jar <オプション>
出力の例は次の通りです。
├─ 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 で終了します。
テストが見つからず、かつコマンドラインオプション --fail-if-no-tests が指定されていた場合は、
ConsoleLauncher はステータスコード 2 で終了します。その他の場合は、終了コードは 0 です。
|
4.3.1. オプション
Usage: ConsoleLauncher [-h] [--disable-ansi-colors] [--fail-if-no-tests] [--scan-modules]
[--scan-classpath[=PATH[;|:PATH...]]]... [--details=MODE]
[--details-theme=THEME] [--reports-dir=DIR]
[--config=KEY=VALUE]... [--exclude-package=PKG]...
[--include-package=PKG]... [-c=CLASS]... [-cp=PATH[;|:PATH...]]...
[-d=DIR]... [-e=ID]... [-E=ID]... [-f=FILE]... [-m=NAME]...
[-n=PATTERN]... [-N=PATTERN]... [-o=NAME]... [-p=PKG]...
[-r=RESOURCE]... [-t=TAG]... [-T=TAG]... [-u=URI]...
Launches the JUnit Platform from the console.
-h, --help Display help information.
--disable-ansi-colors Disable ANSI colors in output (not supported by all terminals).
--details=MODE Select an output details mode for when tests are executed. Use
one of: none, summary, flat, tree, verbose. If 'none' is
selected, then only the summary and test failures are shown.
Default: tree.
--details-theme=THEME Select an output details tree theme for when tests are executed.
Use one of: ascii, unicode. Default: unicode.
-cp, --classpath, --class-path=PATH[;|:PATH...]
Provide additional classpath entries -- for example, for adding
engines and their dependencies. This option can be repeated.
--fail-if-no-tests Fail and return exit status code 2 if no tests are found.
--reports-dir=DIR Enable report output into a specified local directory (will be
created if it does not exist).
--scan-modules EXPERIMENTAL: Scan all resolved modules for test discovery.
-o, --select-module=NAME EXPERIMENTAL: Select single module for test discovery. This
option can be repeated.
--scan-classpath, --scan-class-path[=PATH[;|:PATH...]]
Scan all directories on the classpath or explicit classpath
roots. Without arguments, only directories on the system
classpath as well as additional classpath entries supplied via
-cp (directories and JAR files) are scanned. Explicit classpath
roots that are not on the classpath will be silently ignored.
This option can be repeated.
-u, --select-uri=URI Select a URI for test discovery. This option can be repeated.
-f, --select-file=FILE Select a file for test discovery. This option can be repeated.
-d, --select-directory=DIR Select a directory for test discovery. This option can be
repeated.
-p, --select-package=PKG Select a package for test discovery. This option can be repeated.
-c, --select-class=CLASS Select a class for test discovery. This option can be repeated.
-m, --select-method=NAME Select a method for test discovery. This option can be repeated.
-r, --select-resource=RESOURCE
Select a classpath resource for test discovery. This option can
be repeated.
-n, --include-classname=PATTERN
Provide a regular expression to include only classes whose fully
qualified names match. To avoid loading classes unnecessarily,
the default pattern only includes class names that begin with
"Test" or end with "Test" or "Tests". When this option is
repeated, all patterns will be combined using OR semantics.
Default: [^(Test.*|.+[.$]Test.*|.*Tests?)$]
-N, --exclude-classname=PATTERN
Provide a regular expression to exclude those classes whose fully
qualified names match. When this option is repeated, all
patterns will be combined using OR semantics.
--include-package=PKG Provide a package to be included in the test run. This option can
be repeated.
--exclude-package=PKG Provide a package to be excluded from the test run. This option
can be repeated.
-t, --include-tag=TAG Provide a tag or tag expression to include only tests whose tags
match. When this option is repeated, all patterns will be
combined using OR semantics.
-T, --exclude-tag=TAG Provide a tag or tag expression to exclude those tests whose tags
match. When this option is repeated, all patterns will be
combined using OR semantics.
-e, --include-engine=ID Provide the ID of an engine to be included in the test run. This
option can be repeated.
-E, --exclude-engine=ID Provide the ID of an engine to be excluded from the test run.
This option can be repeated.
--config=KEY=VALUE Set a configuration parameter for test discovery and execution.
This option can be repeated.
4.3.2. 引数ファイル (@ファイル)
プラットフォームによっては、多数のオプションや長い引数をもつコマンドラインを作ろうとすると、 システムのコマンドライン文字数の上限に引っかかるかもしれません。
バージョン1.3から、ConsoleLauncher は 引数ファイル (@ファイル としても知られる)
をサポートします。引数ファイルは、コマンドに与える引数を記述したファイルです。
内部で使用している picocli コマンドラインパーサーは、
@ 文字から始まる引数を見つけると、そのファイルの中身を引数リストとして展開します。
ファイル内の引数は、スペースまたは改行で分割できます。引数自身が空白を含む場合は、
引数全体をダブルクォートかシングルクォートで囲まなければなりません。
例えば、"-f=My Files/Stuff.java" のようになります。
引数ファイルが存在しないか読み込めない場合は、無視されずにそのリテラル文字列が引数として扱われます。
その結果、 "unmatched argument" エラーメッセージが表示される結果になりがちです。
picocli.trace システムプロパティを DEBUG に設定してからコマンドを実行すれば、
このようなエラーをトラブルシュートすることができます。
複数の @ファイル をコマンドラインに指定することもできます。指定するパスは、 カレントディレクトリからの相対パスにするか、絶対パスにします。
実際の引数が @ 文字から始まる場合は、@ をもう1つ追加してエスケープします。
例えば、@@somearg は @somearg と解釈され、展開はされません。
4.4. JUnit Platformの実行にJUnit 4を使う
JUnitPlatform ランナーは、JUnit Platformのプログラミングモデルに従ったテスト
(例えば、JUnit Jupiterのテストクラス)をJUnit 4環境で実行可能にするための
JUnit 4ベースの Runner です。
テストクラスに @RunWith(JUnitPlatform.class) アノテーションを付与すると、
まだJUnit Platformを直接はサポートしていないけれどもJUnit 4ならサポートしている
IDEやビルドシステムでも実行できるようになります。
JUnit PlatformはJUnit 4にない機能をもっているため、特にレポート周り
(表示名 vs. 技術名 を参照)など、
JUnit Platformの機能のサブセットしかサポートすることができません。
しかし、さしあたっては JUnitPlatform ランナーを使って始めるのが簡単です。
|
4.4.1. セットアップ
クラスパスに以下のライブラリとその依存ライブラリが必要です。Group IDやArtifact ID、 バージョンの詳細については、 依存関係メタデータ を参照してください。
4.4.2. 表示名 vs. 技術名
|
訳注:本文書では Display Name に対して 表示名 という訳語を使用し、 本節で登場する Technical Name に対しては 技術名 という訳語を使用した。 しかし、後者は直感的ではないと思うので、適切な訳があればフィードバックをいただきたい。 |
@RunWith(JUnitPlatform.class) ランナーで実行されるクラスにカスタムの
表示名 (display name) を定義するためには、@SuiteDisplayName
アノテーションを付与します。
デフォルトでは、 表示名 はテストの成果物に使われます。しかし、Gradleや
Mavenのようなビルドツールが JUnitPlatform ランナーを使う場合、
生成されるテストレポートには、テストクラスの単純名や
特殊文字を含むカスタム表示名ではなく、完全修飾クラス名のような
技術名 (technical name) が必要になることがあります。
レポート出力で 技術名 を有効にするためには、
@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
は、次のユースケースのために 設定パラメーター をサポートしています。
設定パラメーター は、テキストベースのキーと値のペアで、JUnit Platform で動作するテストエンジンに次のいずれかの方法で渡されます。
-
LauncherDiscoveryRequestBuilderクラスのconfigurationParameter()またはconfigurationParameters()メソッドを使って構築したリクエストをLauncherAPI に渡します。JUnit Platform が提供するツールを使ってテストを実行する場合、設定パラメーターは次のように指定できます。-
Console Launcher:
--configコマンドラインオプションを使います -
Gradle:
systemPropertyまたはsystemPropertiesDSLを使います -
Maven Surefireプロバイダ:
configurationParametersプロパティを使います
-
-
JVMシステムプロパティ
-
JUnit Platform設定ファイル: クラスパスのルートに
junit-platform.propertiesという名前で配置され、JavaのPropertiesファイルと同じシンタックスに従います。
設定パラメーターは、上で定義された順番に従って解決されます。つまり、
Launcher に直接与えられた設定パラメーターの方が、システムプロパティや設定ファイルで
与えられたものより優先されます。同様に、システムプロパティで与えられた設定パラメーターの方が、
設定ファイルで与えられたものより優先されます。
|
4.6. タグ式
タグ式は、 !, &, | 演算子からなるブール式です。
演算子の優先順位を調整するために、( と ) も使えます。
| 演算子 | 意味 | 結合性 |
|---|---|---|
|
not |
right |
|
and |
left |
|
or |
left |
テストに直交するタグを付与している場合、実行するテストを選択するのにタグ式は便利です。 テストの種類 (例えば、micro, integration, end-to-end) と機能 (例えば、 foo, bar, baz) のタグを付与すると、次のようなタグ式が使えます。
| タグ式 | 該当するテスト |
|---|---|
foo |
foo に対するすべてのテスト |
bar | baz |
bar または baz に対するすべてのテスト |
bar & baz |
bar かつ baz に対するすべてのテスト |
foo & !end-to-end |
foo に対するテストのうち、end-to-end レベル以外のすべてのテスト |
(micro | integration) & (foo | baz) |
foo または baz に対するテストのうち、micro または integration レベルのすべてのテスト |
4.7. 標準出力/標準エラー出力のキャプチャ
バージョン1.3から、JUnit Platformは System.out と System.err
への出力をキャプチャする機能をオプトインでサポートします。有効にするには、
junit.platform.output.capture.stdout および junit.platform.output.capture.stderr
設定パラメーター に true を指定するだけです。
加えて、junit.platform.output.capture.maxBuffer を使用することで、
テストやコンテナ毎に使用されるバッファサイズの最大値を設定することができます。
有効にすると、JUnit Platformは該当の出力をキャプチャし、stdout または stderr
キーを使ってレポート情報に格納した上で、テストやコンテナの終了をレポートする直前に
TestExecutionListener インスタンスへ通知します。
キャプチャされた出力は、コンテナやテストを実行するのに使用したスレッドからの出力だけを 含む点に注意してください。その他のスレッドからの出力は除外されます。というのも、 特に テストを並列実行している 場合には、 出力をテストやコンテナ単位でひも付けるのは不可能だからです。
| 出力のキャプチャは、今のところ 実験的な 機能です。JUnitチームがこの機能を改善し、 最終的には 昇格 できるよう、試しに使ってみてフィードバックをください。 |
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 で宣言的に登録された拡張機能は、ソースコード上での宣言順で実行されます。
例えば、MyFirstTests と MySecondTests のテスト実行は、FooExtension と
BarExtension によって まさにこの順序で 拡張されます。
|
5.2.2. 手続き的な登録
開発者は、テストクラスのフィールドに
@RegisterExtension アノテーションを付与することで、
拡張機能を 手続き的に 登録することができます。
拡張機能を @ExtendWith で
宣言的に 登録する場合、拡張機能の設定はアノテーション経由でしか行なえません。
しかし、拡張機能を @RegisterExtension で登録する場合は、
拡張機能のコンストラクタやstaticファクトリメソッド、
ビルダーAPIに引数を渡して 手続き的に 設定することができます。
@RegisterExtension フィールドは、private または null (評価時)
であってはなりませんが、static か非 static かはどちらでもかまいません。
|
staticフィールド
@RegisterExtension フィールドが static の場合、その拡張機能は
@ExtendWith でクラスレベルに登録される拡張機能の後で登録されます。
そのような staticな拡張機能 は、実装する拡張APIに制限がありません。
そのため、staticフィールドとして登録される拡張機能は、
BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor
のようなクラスレベルやインスタンスレベルの拡張APIだけでなく、
BeforeEachCallback などのようなメソッドレベルの拡張APIも実装できます。
次の例では、テストクラスの server フィールドは WebServerExtension
がサポートするビルダーパターンを使って手続き的に初期化されます。設定された
WebServerExtension は、クラスレベルの拡張機能として自動的に登録され、
例えば、すべてのテストを開始する前にサーバーを起動したり、
すべてのテストが完了した後でサーバーを停止したりすることができます。
加えて、@BeforeEach, @AfterEach, @Test メソッドだけでなく、
@BeforeAll や @AfterAll のようなstaticなライフサイクルメソッドも
必要であれば 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());
}
}
Kotlinでのstaticフィールド
プログラミング言語Kotlinには、static フィールドがありません。しかし、
アノテーションを使うと、コンパイラにstaticフィールドを生成させることができます。
先に述べた通り、@RegisterExtension フィールドは private または null
になってはならないため、private フィールドを生成する @JvmStatic
アノテーションをKotlinで 使うことはできません。
代わりに、@JvmField アノテーションを使ってください。
次の例は、前節の WebServerDemo をKotlinに移植したものです。
class KotlinWebServerDemo {
companion object {
@JvmField
@RegisterExtension
val server = WebServerExtension.builder()
.enableSecurity(false)
.build()
}
@Test
fun getProductList() {
// Use WebClient to connect to web server using serverUrl and verify response
val webClient = WebClient()
val serverUrl = server.serverUrl
assertEquals(200, webClient.get("$serverUrl/products").responseStatus)
}
}
インスタンスフィールド
@RegisterExtension フィールドが非static (つまり、インスタンスフィールド)
の場合、その拡張機能はテストクラスがインスタンス化され、登録済みの各
TestInstancePostProcessor がテストインスタンスの事後処理
(拡張機能のインスタンスをアノテーションの付与されたフィールドに注入するなど)
をした後で登録されます。そのため、インスタンスフィールドの拡張機能 が
BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor
のようなクラスレベルやインスタンスレベルの拡張APIを実装しても意味がありません。
デフォルトでは、インスタンスフィールドの拡張機能は @ExtendWith
でメソッドレベルに登録される拡張機能より 後で 登録されます。しかし、
テストクラスが @TestInstance(Lifecycle.PER_CLASS) モードに設定されている場合は、
@ExtendWith でメソッドレベルに登録される拡張機能の 前に 登録されます。
次の例では、テストクラスの docs フィールドは lookUpDocsDir()
メソッドを呼び出した結果を DocumentationExtension のstaticファクトリメソッド
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
自動検出が有効化されると、ServiceLoader の仕組みで検知された拡張機能は、
JUnit Jupiterのグローバルな拡張機能 (例えば、TestInfo や TestReporter
のサポート) の後で拡張機能レジストリに登録されます。
5.3. 条件付きテスト実行
ExecutionCondition は、プログラムの 条件付きテスト実行 のための Extension APIを定めています。
ExecutionCondition は、コンテナ (例えば、テストクラス) がもつテストを与えられた
ExtensionContext で実行すべきか判断するためにコンテナ毎に評価されます。
同様に、ExecutionCondition は、個々のテストメソッドを与えられた ExtensionContext
で実行すべきか判断するためにテスト毎に評価されます。
複数の ExecutionCondition 拡張が登録された場合、無効 と判定する拡張機能が1つでもあれば、
コンテナやテストはただちに無効化されます。別の拡張機能がすでにコンテナやテストを無効化していることがあるので、
ある拡張機能が必ず評価されるという保証はありません。言い換えると、論理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パッケージおよびすべてのサブパッケージにある条件を非アクティブにします。 -
*.MyCondition: 単純クラス名がMyConditionである条件をすべて非アクティブにします。 -
*System*: 単純クラス名にSystemを含む条件をすべて非アクティブにします。 -
org.example.MyCondition: FQCNがorg.example.MyConditionである条件を非アクティブにします。
5.4. テストインスタンスのファクトリ
TestInstanceFactory は、テストクラスのインスタンスを 生成する
ための Extension APIを定めています。
一般的なユースケースとしては、DI(依存性注入)フレームワークからテストインスタンスを取得したり、 テストクラスのインスタンス生成にstaticファクトリメソッドを呼んだりすることです。
TestInstanceFactory が登録されていない場合は、
フレームワークは単にテストクラスの 唯一の コンストラクタを実行して初期化します。
このとき、コンストラクタの引数は、登録済みの ParameterResolver 拡張で解決されます。
TestInstanceFactory を実装する拡張機能は、テストインターフェースや
トップレベルのテストクラス、あるいは @Nested テストクラスに対しても登録できます。
|
|
5.5. テストインスタンスの後処理
TestInstancePostProcessor は、テストインスタンスの 後処理 をするための Extension
APIを定めています。
よくあるユースケースは、テストインスタンスへの依存性注入や、テストインスタンスのカスタムの 初期化メソッド呼び出しなどがあります。
具体例は、MockitoExtension や SpringExtension のソースコードを参照してください。
5.6. 引数の解決
ParameterResolver は、実行時に動的に引数を解決するための Extension APIを定めています。
テストコンストラクタや @Test, @RepeatedTest, @ParameterizedTest, @TestFactory,
@BeforeEach, @AfterEach, @BeforeAll, @AfterAll メソッドが引数をもつ場合、
その引数は ParameterResolver によって実行時に 解決 されなければいけません。
ParameterResolver は、ビルトインのもの (TestInfoParameterResolver 参照) または
ユーザーが登録したもの のどちらかです。一般的に言うと、
引数は 名前、型、アノテーション、あるいはその組み合わせで解決されます。
具体例は、CustomTypeParameterResolver や CustomAnnotationParameterResolver
のソースコードを参照してください。
|
JDK 9以前の そのため、
|
5.7. テストライフサイクルコールバック
以下のインターフェースは、テスト実行のライフサイクルにおける様々なタイミングでテストを拡張するための
APIを定めています。詳細は、以降の節にあるサンプルと、org.junit.jupiter.api.extension パッケージにある
これらのインターフェースのJavadocを参照してください。
|
複数のExtension APIを実装する
拡張機能の開発者は、1つの拡張機能でこれらのインターフェースをいくつでも実装することができます。
具体例は、SpringExtension のソースコードを参照してください。
|
5.7.1. テスト実行前後のコールバック
BeforeTestExecutionCallback と AfterTestExecutionCallback は、
テストメソッドが実行される 直前 と 直後 に実行されるような振る舞いを追加したい場合の
Extension API を定めています。実行時間の計測や、処理のトレースなどのユースケースに適しています。
もしも @BeforeEach メソッドや @AfterEach メソッドの 前後 に呼び出されるコールバックが
必要な場合は、代わりに BeforeEachCallback や AfterEachCallback を実装してください。
次の例は、テストメソッドの実行時間を計測してログ出力するために、
これらのコールバックを利用する方法を示しています。TimingExtension は、
BeforeTestExecutionCallback と AfterTestExecutionCallback の両方を実装しています。
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
を登録しているので、テストを実行すると時間計測が有効になります。
@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.8. 例外ハンドリング
TestExecutionExceptionHandler は、テスト実行中に投げられた例外を扱うための
Extension 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.9. テストテンプレートに実行コンテキストを与える
@TestTemplate メソッドは、TestTemplateInvocationContextProvider
が少なくとも1つ登録されている場合のみ実行されます。これらのプロバイダは、
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.10. 拡張機能の状態を保持する
通常、拡張機能は一度だけしかインスタンス化されません。そのため、
拡張機能のある実行から次の実行までどのように状態を保持するのか?が問題となります。
ExtensionContext APIは、まさにこの目的のために Store を提供しています。
拡張機能は、値を Store に保存しておいて、あとで取得することができます。
メソッドレベルのスコープで Store を使用する例については、
TimingExtension
を参照してください。
テスト実行の間に ExtensionContext に保存された値は、その外側の ExtensionContext
では利用できないことを覚えておくのは重要です。ExtensionContext はネストされうるので、
内側の拡張コンテキストのスコープも制限されることがあります。
Store 経由で保存・取得するために使えるメソッドの詳細については、
関連する JavaDoc を参照してください。
ExtensionContext.Store.CloseableResourceCloseableResource のインスタンスである場合は、その close()
メソッドが呼び出されて通知されます。
|
5.11. 拡張機能でサポートされるユーティリティ
junit-platform-commons は、アノテーションやクラス、リフレクション、
クラスパスの探索処理に使える メンテナンスされた ユーティリティメソッドを含む
org.junit.platform.commons.support というパッケージを提供しています。
TestEngine や Extension の作者は、JUnit Platformの振る舞いに合わせるために、
これらのメソッドを利用することが推奨されます。
5.11.1. アノテーションサポート
AnnotationSupport は、アノテーションが付与された要素(例えば、パッケージやアノテーション、
クラス、インターフェース、コンストラクタ、メソッド、フィールド)を操作するstatic
なユーティリティメソッドを提供します。その中には、
ある要素に特定のアノテーションやメタアノテーションが付与されているかチェックしたり、
特定のアノテーションを探したり、クラスやフィールドからアノテーションの付与されたメソッドを
探したりするメソッドが含まれます。これらのメソッドのいくつかは、
アノテーションを探すために実装インターフェースやクラス階層を探索します。
さらなる詳細は、AnnotationSupport のJavaDocを確認してください。
5.11.2. クラスサポート
ClassSupport は、クラス(つまり、java.lang.Class のインスタンス)を操作する
staticなユーティリティメソッドを提供します。詳細は、ClassSupport のJavaDoc
を参照してください。
5.11.3. リフレクションサポート
ReflectionSupport は、標準JDKのリフレクションとクラスロードの仕組みを強化する
staticなユーティリティメソッドを提供します。
その中には、指定された述語にマッチするクラスを求めてクラスパスを探索したり、
あるクラスをロードして新しいインスタンスを作成したり、メソッドを探して実行したりする
メソッドが含まれます。
これらのメソッドのいくつかは、マッチするメソッドを特定するためにクラス階層を探索します。
さらなる詳細は、ReflectionSupport のJavaDocを確認してください。
5.12. ユーザーコードと拡張機能の相対的な実行順序
1つ以上のテストメソッドを含むテストクラスを実行する場合、ユーザーが指定するテストメソッドと ライフサイクルメソッドに加えて、多くの拡張機能コールバックが呼び出されます。次の図は、 ユーザーコードと拡張機能コードの相対的な実行順序を示しています。
ユーザーが提供するテストメソッドとライフサイクルメソッドはオレンジ色、 拡張機能が提供するコールバックコードは青色で示されています。灰色のボックスは、 単一のテストメソッドの実行を表し、テストクラス内のテストメソッド毎に繰り返されます。
次の表は、ユーザーコードと拡張機能コード の図の12ステップをさらに説明しています。
| ステップ | インターフェース/アノテーション | 説明 |
|---|---|---|
1 |
|
コンテナのすべてのテストが実行される前に実行される拡張機能のコード |
2 |
|
コンテナのすべてのテストが実行される前に実行されるユーザーコード |
3 |
|
各テストが実行される前に実行される拡張機能のコード |
4 |
|
各テストが実行される前に実行されるユーザーコード |
5 |
|
テストが実行される直前に実行される拡張機能のコード |
6 |
|
実際のテストメソッドとなるユーザーコード |
7 |
|
テストの間に投げられた例外をハンドリングするための拡張機能のコード |
8 |
|
テストが実行され、その例外ハンドラが処理された直後に実行される拡張機能のコード |
9 |
|
各テストが実行された後に実行されるユーザーコード |
10 |
|
各テストが実行された後に実行される拡張機能のコード |
11 |
|
コンテナのすべてのテストが実行された後に実行されるユーザーコード |
12 |
|
コンテナのすべてのテストが実行された後に実行される拡張機能のコード |
最も単純な場合は、実際のテストメソッドだけが実行されます (ステップ 6)。 ほかのすべてのステップは、ライフサイクルコールバックに対するユーザーコードや拡張機能コードの 有無によって省略可能です。様々なライフサイクルコールバックに関する詳細については、 各アノテーションや拡張機能に対する JavaDoc を参照してください。
6. JUnit 4からの移行
JUnit Jupiterのプログラミングモデルと拡張モデルは、Rule や Runner
のようなJUnit 4の機能をネイティブにはサポートしていません。しかし、
ソースコードのメンテナがJUnit Jupiterへ移行するためにすべての既存テストやテスト拡張、
カスタムビルド基盤を書き換えることを求めているわけではありません。
代わりに、JUnit Platform上でJUnit 3またはJUnit 4ベースの既存テストを実行できる
ようにする JUnit Vintageテストエンジン を通して、なだらかな移行パスを提供しています。
JUnit Jupiter固有のクラスやアノテーションはすべて新設された org.junit.jupiter
パッケージ配下にあるため、JUnit 4とJUnit Jupiterが同時にクラスパスに含まれていても
コンフリクトは発生しません。そのため、既存のJUnit 4テストをJUnit Jupiter
テストと並行してメンテナンスしても安全です。さらに、JUnitチームはJUnit 4.x
系のメンテナンスとバグフィックスリリースを継続しているため、
開発者は自身のスケジュールに合わせてJUnit Jupiterに移行する十分な時間があります。
6.1. JUnit Platform上でJUnit 4テストを実行する
junit-vintage-engine がテストランタイムパスに含まれていることを確認してください。
そうすれば、JUnit Platformランチャーが、JUnit 3およびJUnit 4のテストを自動的に
ピックアップしてくれます。
junit5-samples リポジトリにあるサンプルプロジェクトを
参照して、GradleやMavenでどのように実現するか確認してください。
6.1.1. カテゴリのサポート
@Category アノテーションの付与されたテストクラスやテストメソッドに対しては、
JUnit Vintageテストエンジン はカテゴリの完全修飾クラス名を該当テストのタグとみなします。
例えば、テストメソッドに @Category(Example.class) アノテーションが付与されている場合、
"com.acme.Example" でタグ付けされます。JUnit 4の Categories ランナーと同様に、
この情報は実行するテストのフィルタリングに使われます (詳細は、テストを実行する 参照)。
6.2. 移行のヒント
既存の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に置き換えられました。 部分的なルールのサポートについては、次節を参照してください。
6.3. JUnit 4のルールの限定的なサポート
前述したように、JUnit JupiterはJUnit 4のルールをサポートしていませんし、 その予定もありません。しかし、JUnitチームは、多くの組織が(特に大きな組織ほど) カスタムルールを使ったJUnit 4のコードベースを保持していることを理解しています。 このような組織に段階的な移行パスを用意するため、JUnitチームはJUnit Jupiter でJUnit 4のルールをサポートすることを決めました。 このサポートはアダプタをベースにしており、JUnit Jupiterの拡張モデルと意味的に 互換性があるルールに限定されています。 例えば、テストの実行フローを完全に変えてしまうようなことがないものです。
JUnit Jupiterの junit-jupiter-migrationsupport モジュールは、
今のところ次の3つの Rule とそのサブクラスをサポートしています。
-
org.junit.rules.ExternalResource(org.junit.rules.TemporaryFolderも含む) -
org.junit.rules.Verifier(org.junit.rules.ErrorCollectorも含む) -
org.junit.rules.ExpectedException
JUnit 4と同様に、Ruleアノテーションが付与されたフィールドとメソッドをサポートします。
これらのクラスレベルの拡張機能を使うことで、レガシーコードがもつ Rule
実装をJUnit 4のimport文も含めて 変更せずにそのままにしておく ことができます。
この限定的な形での Rule サポートは、クラスレベルのアノテーション
org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport
を使って有効にすることができます。このアノテーションは、
VerifierSupport, ExternalResourceSupport, ExpectedExceptionSupport
のすべての移行サポート拡張を有効にするための 合成アノテーション になっています。
なお、JUnit 5に対する新しい拡張機能を開発するときには、JUnit 4 のルールベースのモデルではなく、JUnit Jupiterの拡張モデルを使ってください。
JUnit JupiterにおけるJUnit 4の Rule サポートは、今のところ
実験的な 機能です。詳細は、実験的なAPI
の表を確認してください。
|
7. Advanced Topics
7.1. JUnit Platform Launcher API
One of the prominent goals of JUnit 5 is to make the interface between JUnit and its programmatic clients – build tools and IDEs – more powerful and stable. The purpose is to decouple the internals of discovering and executing tests from all the filtering and configuration that’s necessary from the outside.
JUnit 5 introduces the concept of a Launcher that can be used to discover, filter, and
execute tests. Moreover, third party test libraries – like Spock, Cucumber, and FitNesse
– can plug into the JUnit Platform’s launching infrastructure by providing a custom
TestEngine.
The launcher API is in the junit-platform-launcher module.
An example consumer of the launcher API is the ConsoleLauncher in the
junit-platform-console project.
7.1.1. Discovering Tests
Introducing test discovery as a dedicated feature of the platform itself will (hopefully) free IDEs and build tools from most of the difficulties they had to go through to identify test classes and test methods in the past.
Usage Example:
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.LauncherConfig;
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);
There’s currently the possibility to select classes, methods, and all classes in a package or even search for all tests in the classpath. Discovery takes place across all participating test engines.
The resulting TestPlan is a hierarchical (and read-only) description of all engines,
classes, and test methods that fit the LauncherDiscoveryRequest. The client can
traverse the tree, retrieve details about a node, and get a link to the original source
(like class, method, or file position). Every node in the test plan has a unique ID
that can be used to invoke a particular test or group of tests.
7.1.2. Executing Tests
To execute tests, clients can use the same LauncherDiscoveryRequest as in the discovery
phase or create a new request. Test progress and reporting can be achieved by registering
one or more TestExecutionListener implementations with the Launcher as in the
following example.
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);
There is no return value for the execute() method, but you can easily use a listener to
aggregate the final results in an object of your own. For an example see the
SummaryGeneratingListener.
7.1.3. Plugging in your own Test Engine
JUnit currently provides two TestEngine implementations.
-
junit-jupiter-engine: The core of JUnit Jupiter. -
junit-vintage-engine: A thin layer on top of JUnit 4 to allow running vintage tests with the launcher infrastructure.
Third parties may also contribute their own TestEngine by implementing the interfaces
in the junit-platform-engine module and registering their engine. By default, engine
registration is supported via Java’s java.util.ServiceLoader mechanism. For example,
the junit-jupiter-engine module registers its
org.junit.jupiter.engine.JupiterTestEngine in a file named
org.junit.platform.engine.TestEngine within the /META-INF/services in the
junit-jupiter-engine JAR.
HierarchicalTestEngine is a convenient abstract base implementation (used by
the junit-jupiter-engine) that only requires implementors to provide the logic for
test discovery. It implements execution of TestDescriptors that implement the Node
interface, including support for parallel execution.
|
|
The
junit- prefix is reserved for TestEngines from the JUnit TeamThe JUnit Platform
|
7.1.4. Plugging in your own Test Execution Listener
In addition to the public Launcher API method for registering test execution
listeners programmatically, by default custom TestExecutionListener implementations
will be discovered at runtime via Java’s java.util.ServiceLoader mechanism and
automatically registered with the Launcher created via the LauncherFactory. For
example, an example.TestInfoPrinter class implementing TestExecutionListener and
declared within the
/META-INF/services/org.junit.platform.launcher.TestExecutionListener file is loaded and
registered automatically.
7.1.5. Configuring the Launcher
If you require fine-grained control over automatic detection and registration of test
engines and test execution listeners, you may create an instance of LauncherConfig and
supply that to the LauncherFactory.create(LauncherConfig) method. Typically an instance
of LauncherConfig is created via the built-in fluent builder API, as demonstrated in
the following example.
LauncherConfig launcherConfig = LauncherConfig.builder()
.enableTestEngineAutoRegistration(false)
.enableTestExecutionListenerAutoRegistration(false)
.addTestEngines(new CustomTestEngine())
.addTestExecutionListeners(new CustomTestExecutionListener())
.build();
Launcher launcher = LauncherFactory.create(launcherConfig);
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectPackage("com.example.mytests"))
.build();
launcher.execute(request);
8. APIの進化
JUnit 5の主要な目的の1つは、JUnitが多くのプロジェクトで使われていたとしても、 メンテナがJUnitを進化させられるようにすることです。 JUnit 4では多くの部品が元々は内部用途で追加されましたが、 外部の拡張機能やツールの作者から使われることになりました。 そのせいで、JUnit 4の変更はとりわけ難しく、ときには不可能になりました。
JUnit 5がすべての公開インターフェースやクラス、メソッドに対して明確に定義された ライフサイクルを導入したのはこのためです。
8.1. APIバージョンとステータス
公開されているモジュールは <major>.<minor>.<patch> というバージョン番号をもち、
すべての公開インターフェース、クラス、メソッドには @API Guardian プロジェクトの
@API アノテーションが付与されています。このアノテーションの status 属性には、
次のいずれかの値が設定されています。
| ステータス | 説明 |
|---|---|
|
JUnit自身以外のコードで使ってはならない。事前予告なしに削除されうる。 |
|
もう使うべきではない。次のマイナー (minor) リリースで削除されうる。 |
|
フィードバックがほしい実験的な新機能を意味する。注意して使うべき。 |
|
現在のメジャー (major) バージョンの 少なくとも 次のマイナー (minor)
リリースでは、後方互換性がない方法で変更されることがない機能を意味する。
削除予定になった場合は、まず最初に |
|
現在のメジャーバージョン ( |
@API アノテーションが型に付与された場合、その型のすべての公開メンバーに適用されているとみなします。
個々のメンバーは、より低い安定度をもつ異なる status 属性を宣言することができます。
8.2. 実験的なAPI
次の表は、@API(status = EXPERIMENTAL) で現在 実験的 と指定されたAPIを一覧しています。
これらのAPIを使う場合は注意してください。
| Package Name | Type Name | Since |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8.3. 非推奨のAPI
次の表は、@API(status = DEPRECATED) で現在 非推奨 と指定されたAPIを一覧しています。
非推奨になったAPIは今後のリリースで削除される可能性が高いため、可能な限り使わないようにしてください。
| Package Name | Type Name | Since |
|---|---|---|
|
|
|
|
|
|
8.4. @APIツールサポート
@API Guardian プロジェクトは、 @API アノテーションが付与されたAPIの提供者や利用者のために、
ツールによるサポートを計画しています。例えば、JUnitのAPIが @API
アノテーションの宣言にしたがって使われているかをチェックする手段を提供するなどです。
9. コントリビューター
GitHubで直接 現在のコントリビューター を参照してください。
10. リリースノート
リリースノートは、 こちら から参照できます。