S-JIS[2017-09-14/2022-08-19] 変更履歴
JUnit5のテストクラスの記述方法について。
|
|
JUnit5(Jupiter)では、テストクラスはpublicにする必要は無い。
クラス名は何でもいいが、gradlew testコマンドでテストを実行する場合、デフォルトではクラス名の末尾がTest,
Testsのものだけが実行対象となる。
戻り型がvoidで@Testアノテーションを付けたメソッドがテスト対象になる。
(テストメソッドもpublicにする必要は無い)
また、抽象クラスのメソッドやインターフェースのデフォルトメソッドに@Testアノテーションを付けてテストメソッドとすることが出来る。
これらの抽象クラスやインターフェースを継承したクラスを用意すると、抽象クラスやインターフェースのテストメソッドも実行される。
@Timeoutアノテーションを付けると、テストが長時間かかったときにタイムアウトさせることが出来る。(JUnit5.5以降)[2022-08-19]
テストクラスの内部クラスに@Nestedアノテーションを付けると、そのクラスにもテストメソッドを記述できる。
(テストをグルーピング(分類)して書ける。たぶん可読性の向上が目的)
テストクラスやテストメソッドにタグを付けることが出来る。
gradlew testコマンドの実行時に、特定のタグが付いたテストだけ実行したり、あるいは逆に除外したりすることが出来る。
デフォルトでは、テストメソッドを実行する毎にテストクラスのインスタンスが作られる。
テストクラス毎に1回だけインスタンスを生成するようにすることも出来る。
→テストインスタンスのライフサイクル
テストクラス毎に、全テストの実行前に呼ばれる処理には@BeforeAllアノテーションを付ける。
全テストの実行後に呼ばれる処理には@AfterAllアノテーションを付ける。
各テスト毎に、テスト実行前に呼ばれる処理には@BeforeEachアノテーションを付ける。
テスト実行後に呼ばれる処理には@AfterEachアノテーションを付ける。
テストを実行しないクラスやメソッドには@Disabledアノテーションを付ける。
引数でコメントを入れることも出来る。
テストメソッドには@Testアノテーションを付ける。
テストクラスやテストメソッドには@DisplayNameアノテーションでテスト名を付けることが出来る。
テスト結果のログにはこの名称が表示される。
デフォルトはクラス名やメソッド名(引数の型名付き)。
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.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo;
class BeforeAfterTest { @BeforeAll // JUnit4の @BeforeClass static void beforeAllTests(TestInfo testInfo) { System.out.printf("before all [%s]\n", testInfo.getDisplayName()); } @AfterAll // JUnit4の @AfterClass static void afterAllTests(TestInfo testInfo) { System.out.printf("after all [%s]\n", testInfo.getDisplayName()); }
@BeforeEach // JUnit4の @Before void beforeEachTest(TestInfo testInfo) { System.out.printf("before each [%s]\n", testInfo.getDisplayName()); } @AfterEach // JUnit4の @After void afterEachTest(TestInfo testInfo) { System.out.printf("after each [%s]\n", testInfo.getDisplayName()); }
@Test @DisplayName("test1 !") void test1() { System.out.println("test1"); } @Test void test2(TestInfo testInfo) { System.out.println("test2 " + testInfo); } @Test @Disabled // JUnit4の @Ignore void test3() { System.out.println("test3"); } }
@BeforeAll/@AfterAllや@BeforeEach/@AfterEachおよび@Testアノテーションを付けたメソッドには、引数にTestInfoを指定することが出来る。
import org.junit.jupiter.api.TestInfo;
@BeforeEach void beforeEachTest(TestInfo testInfo) { System.out.printf("before each [%s]\n", testInfo.getDisplayName()); }
TestInfoからは、テスト対象の情報を取得できる。
メソッド | 内容 | |
---|---|---|
String |
getDisplayName() |
テストの表示名 @DisplayNameが付けられている場合はその名称、無ければテストクラス名あるいはテストメソッド名。 |
Set<String> |
getTags() |
テストに付けられているタグ |
Optional<Class<?>> |
getTestClass() |
テストクラス |
Optional<Method> |
getTestMethod( |
テストメソッド |
テストクラスやテストメソッドに@Tagアノテーションでタグを付けることが出来る。
import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test;
class TagTest { @Tag("tag1") @Test void test1() { System.out.println("tag1-test1"); } @Tag("tag2") @Test void test2() { System.out.println("tag2-test2"); } @Tag("tag2") @Tag("tag3") @Test void test3() { System.out.println("tag12-test3"); } }
ちなみに、@Tagアノテーションは一箇所に複数指定することが出来る。
gradlew testコマンドでテストを実行する場合、build.gradleのjunitPlatformのfiltersのtagsでタグを指定することが出来る。
junitPlatform { filters { tags { include 'tag1', 'tag3' // exclude 'tag2' } } }
includeを指定すると、そのタグが付いたテストのみ実行する。
excludeを指定すると、そのタグが付いたテストは除外される。
内部クラスに@Nestedアノテーションを付けると、そのクラスもテスト対象になる。
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test;
class NestTest { @Test void test1() { System.out.println("nest0"); }
@Nested class Nest1 { @Test void test2() { System.out.println("nest1"); } } }
ただし、内部クラスがstaticクラスの場合はテスト対象にならない。
(エラーにもならず、ただ無視されるのみ)
抽象クラスのメソッドやインターフェースのデフォルトメソッドもテストメソッドにすることが出来る。
(ドキュメントにはインターフェースにしか言及が無いが、抽象クラスも試したら出来た)
抽象クラス・インターフェースはインスタンス化できないので、これらを継承したテストクラスを用意する必要はあるが、
そのテストクラスを実行すると、抽象クラスやインターフェースのテストメソッドも実行される。
interface InterfaceExample { public int get(); public int inc(); @Test default void test() { int value1 = get(); int value2 = inc(); assertEquals(value1 + 1, value2); } }
class InterfaceTest implements InterfaceExample { @Override public int get() { return 1; } @Override public int inc() { return 2; } }
デフォルトでは、テストメソッドを実行する毎にテストクラスのインスタンスを生成する。
これを、テストクラス毎に1回だけインスタンスを生成するようにすることが出来る。
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; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.TestInstance; // @TestInstance(TestInstance.Lifecycle.PER_METHOD) @TestInstance(TestInstance.Lifecycle.PER_CLASS) class BeforeAfterTest { public BeforeAfterTest() { System.out.println("concstruct"); }
@BeforeAll static void beforeAllTests(TestInfo testInfo) { System.out.printf("before all [%s]\n", testInfo.getDisplayName()); } @AfterAll static void afterAllTests(TestInfo testInfo) { System.out.printf("after all [%s]\n", testInfo.getDisplayName()); }
@BeforeEach void beforeEachTest(TestInfo testInfo) { System.out.printf("before each [%s]\n", testInfo.getDisplayName()); } @AfterEach void afterEachTest(TestInfo testInfo) { System.out.printf("after each [%s]\n", testInfo.getDisplayName()); }
@Test void test1() { System.out.println("test1"); } @Test void test2(TestInfo testInfo) { System.out.printf("test2 [%s]\n", testInfo.getDisplayName()); } @Test @Disabled("実行しない") void test3() { System.out.println("test3"); } }
↓実行結果
Lifecycle.PER_METHOD | Lifecycle.PER_CLASS |
---|---|
before all [BeforeAfterTest] |
concstruct |
test3は実行されないが、インスタンスは生成される。 | コンストラクターは1回しか呼ばれていない。 |
@BeforeAll・@AfterAllアノテーションを付けるメソッドは、インスタンスメソッドにする(staticを付けない)ことも出来る。
ただしその場合は、ライフサイクルをPRE_CLASSにする必要がある。
ライフサイクルがPRE_METHOD(デフォルト)のままだと、テスト実行時に以下のような例外が発生する。
org.junit.platform.commons.JUnitException: @BeforeAll method 'void com.example.BeforeAfterTest.beforeAllTests(org.junit.jupiter.api.TestInfo)' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS). at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.assertStatic(LifecycleMethodUtils.java:59) at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.lambda$findMethodsAndAssertStatic$0(LifecycleMethodUtils.java:83) at java.util.ArrayList.forEach(ArrayList.java:1249) at java.util.Collections$UnmodifiableCollection.forEach(Collections.java:1080) at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findMethodsAndAssertStatic(LifecycleMethodUtils.java:83) at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeAllMethods(LifecycleMethodUtils.java:42) at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.prepare(ClassTestDescriptor.java:119) at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.prepare(ClassTestDescriptor.java:61) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:60) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$null$2(HierarchicalTestExecutor.java:92) 〜
テストクラスに@TestInstanceアノテーションを付けない場合のデフォルトは、設定により変更することが出来る。
junit.jupiter.testinstance.lifecycle.default = PER_CLASS
ちなみに、設定値は大文字でも小文字でも大丈夫な模様。
gradlew testコマンドでテストを実行する場合は、build.gradleのjunitPlatformのconfigurationParameterで設定することも出来る。
junitPlatform { configurationParameter 'junit.jupiter.testinstance.lifecycle.default', 'PER_CLASS' }
同一のテストを繰り返し実行したい場合は、@Testアノテーションの代わりに@RepeatedTestアノテーションを使う。
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.TestInfo;
class RepeatTest { @RepeatedTest(5) @DisplayName("repeat1") void test1(TestInfo testInfo) { System.out.println(testInfo.getDisplayName()); }
@RepeatedTest(value = 5, name = "{displayName} {currentRepetition}/{totalRepetitions}") @DisplayName("repeat2") void test2(TestInfo testInfo) { System.out.println(testInfo.getDisplayName()); } }
この場合、@DisplayNameで付けたテスト名はTestInfo#getDisplayName()では取得できない。
@RepeatedTestの引数nameで付けた名前がTestInfo#getDisplayName()で取得できる。
@RepeatedTestの引数nameには、波括弧で特別な値を指定することが出来る。
変数 | 説明 |
---|---|
{displayName} |
テスト名(@DisplayNameで付けた名前) |
{currentRepetition} |
テストの番号(1から開始) |
{totalRepetitions} |
テストの総回数 |
↓実行結果
repetition 1 of 5 repetition 2 of 5 repetition 3 of 5 repetition 4 of 5 repetition 5 of 5 repeat2 1/5 repeat2 2/5 repeat2 3/5 repeat2 4/5 repeat2 5/5
テストの回数毎にテスト値を変えたい場合は、@ParameterizedTestアノテーションを使う。