Junit5の新たな機能いろいろ（前編)

# 概要
* Junit5の公式docの"Writing Test"の章で紹介されている機能を紹介します。（執筆中）
* なお後半の執筆予定はありません。
* https://junit.org/junit5/docs/current/user-guide/#writing-tests

# カスタムアノテーションにメタタグを定義して利用できる。

* カスタムアノテーションの作成

```java:Fast.java
import org.junit.jupiter.api.Tag;

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

* テストメソッドに設定

```java:SomeTest.java
class SomeTest {

    @Test
    @Fast
    void succeedingTest() {
    }
}

```

* 解説
    * メソッドに`@Tag("fast")`と書かずに、`@Fast`と書くことができる。
    * 何がうれしいかはいまいちわからない。

# テストの実行結果に好きな文字列を表示できる。

```java:SomeTest.java

@DisplayName("A special test case")
class SomeTest {
    @Test
    @DisplayName("Custom test name containing spaces")
    void testWithDisplayNameContainingSpaces() {
    }

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

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

```

* 実行結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/159985/4b75bd66-004f-0afb-2d1c-8bc103e3c3bc.png)

* Junit4まで

![image.png](https://qiita-image-store.s3.amazonaws.com/0/159985/f752987c-729f-a084-ad0a-e0c9743d55c0.png)



# assertion関連の機能

```java:Person.java
    private class Person{
        String firstName;
        String lastName;

        public Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }
    }
```

```java
class sample {

    private Person person = new Person("first", "last");

    @Test
    void standardAssertions() {
        assertEquals(2, 2);
        assertEquals(3, 4, "アサーション失敗時のメッセージを任意に設定できる。（失敗するとここに設定した文字が表示される。）.");
        assertTrue('a' < 'b', () -> "不要に複雑なメッセージを避けるためアサーションのメッセージは遅延評価される");
    }

    @Test
    void groupedAssertions() {
        //　この書き方だとアサーションは頭で失敗しても、すべて評価される。(Junit4の書き方だとfailしたステップで終了。)
        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"))
                    );
                },
                () -> {
                    // このように書くことで、firstNameのアサーション結果とは無関係に
                    // こちらのアサーションを実行できる。
                    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), () -> {
            // ２秒以内に処理が終わったらこのアサーションは成功する。
        });
    }

    @Test
    void timeoutExceeded() {
        assertTimeout(ofMillis(10), () -> {
            // この処理は100ミリかかるので失敗する。
            Thread.sleep(100);
        });
    }
}
```

# "前提とする"というアサーション
## assumeTrue、assumingThat
* 
```java
    @Test
    void testOnlyOnCiServer() {
        assumeTrue("CI".equals(System.getenv("ENV")));
        // remainder of test
    }

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

    @Test
    void testInAllEnvironments() {
        assumingThat("CI".equals(System.getenv("ENV")),
            () -> {
                // CIサーバーの時だけ実施される。
                assertEquals(2, 2);
            });

        // 全部の条件で実施される。
        assertEquals("a string", "a string");
    }
```
* 解説
    * 特定の値を持つ場合のみ、あるテストを実施するというテストが可能。(assumingthat)
    * assumeTrueとassertTrueはあんまり違いがわからない。

# 特定の状況においてのみメソッドやクラスを実行する。
* `@EnabledOnOs`、`@DisabledOnOs`

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

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

@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
    // ...
}
```
* 解説
    * テストが実行されるOSによって、テストを実施するかどうかをクラス、メソッドレベルで指定することができる。
    * [ExecutionCondition](https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/extension/ExecutionCondition.html)を拡張することでこういった条件を独自に設定可能。
    * そのほかにも`@EnabledOnJre`、`@DisabledOnJre`、`@EnabledIfSystemProperty`、`@DisabledIfSystemProperty`といったJavaバージョンやシステムプロパティの値によって切り分けるものもある。他にもいろいろあるのでサンプルだけ乗せておきます。

```java
@Test // Static JavaScript expression.
@EnabledIf("2 * 3 == 6")
void willBeExecuted() {
    // ...
}

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

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

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

@Test // Multi-line script, custom engine name and custom reason.
@EnabledIf(value = {
                "load('nashorn:mozilla_compat.js')",
                "importPackage(java.time)",
                "",
                "var today = LocalDate.now()",
                "var tomorrow = today.plusDays(1)",
                "tomorrow.isAfter(today)"
            },
            engine = "nashorn",
            reason = "Self-fulfilling: {result}")
void theDayAfterTomorrow() {
    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plusDays(1);
    assertTrue(tomorrow.isAfter(today));
}
```

# タグ付けとフィルタリング
* `@Tag("model")`みたいなのが使えて、これでフィルターできるっぽい。

# テストインスタンスのライフサイクル
* 今までのJunitはテストメソッドが実行されるたびにインスタンスが再生成されていた。
* それを、メソッド単位ではなくクラス単位で行えるようになった。
    * `@TestInstance(Lifecycle.PER_CLASS)`
* これを設定すると`@BeforeAll`,`@AfterAll`が使えるようになる。
* JVM引数でもこの挙動は変えられる。
    * -Djunit.jupiter.testinstance.lifecycle.default=per_class

# ネストクラスでテストをグループ化できるようになった
```java
class TestingAStackDemo {

    Stack<Object> stack;

    @Nested
    class WhenNew {

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

        @Test
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

        @Nested
        class AfterPushing {

            String anElement = "an element";

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

            @Test
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }

        }
    }
}
```
# コンストラクターとメソッドが引数を受け取れるようになった。
* `TestInfo`、` RepetitionInfo`、 `TestReporter`
    * これらの型の引数をメソッドに設定すると`@DisplayName`に設定した値などがとれる。
    * TestReporterは実行結果のレポートに情報を出力できる。（stdoutではなくこっちを使う。）

* `@ExtendWith`で拡張できる。mockitoとかspringのもある。（ここ追記する。）
    * [MockitoExtension](https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java)
    * [SpringExtension](https://github.com/spring-projects/spring-framework/blob/master/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java)