JUnit4とJUnit5の比較(便利な機能編)

## 概要
JUnit4の便利な機能が
JUnit5でどのように実装されているか、確かめてみました。
古いテストケースをJUnit5に移すときの自分用備忘録。
諸々の説明は[JUnit5公式ドキュメント](https://junit.org/junit5/docs/current/user-guide/)を参照してください。

## 検証環境
検証環境は次の通りです。
OS : Windows 7 Home Edition 64bit
Java : JavaSE 15
IDE : STS 4.10.0
その他 : Spring Boot 2.5.5
JUnit4, 5用に別々にプロジェクトを作成

### 注意事項 
Spring Bootをウィザードで作った場合、
そのままだとJUnit4が使えません。
対策としてプロジェクトを作ってから、
pom.xmlからJUnit4を追加、
spring-boot-starter-testへの依存関係を
コメントアウトしました。

## 新旧比較する機能

 - 一時フォルダ作成
 - テスト中メソッド名取得
 - エラーが出ても全ケースを実施
 - 同じテストケースを様々なパターンで繰り返し実施

### 一時フォルダ作成

```java:JUnit4(抜粋)
	@Rule
	public TestName testName = new TestName();
	
	@Test
	public void testAboutTemporaryFile() throws Exception {
		final String testString = "This is a test.";
		File file = tempFolder.newFile("sample.txt");

		BufferedWriter writer = new BufferedWriter(
			new OutputStreamWriter(
				new FileOutputStream(file)));
		writer.write(testString);
		writer.close();
		
		BufferedReader reader = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(file)));
		String line = "";
		String tmp;
		while ((tmp = reader.readLine()) != null) {
			line += tmp;
		}
		reader.close();
		assertEquals(testString, is(line));
	}
```

```java:JUnit5(抜粋)
	@TempDir
	File tempFolder;

	@Test
	public void testAboutTemporaryFile() throws Exception {

		final String testString = "This is a test.";
		File file = new File(tempFolder, "sample.txt");

		BufferedWriter writer = new BufferedWriter(
			new OutputStreamWriter(
				new FileOutputStream(file)));
		writer.write(testString);
		writer.close();

		BufferedReader reader = new BufferedReader(
			new InputStreamReader(
				new FileInputStream(file)));
		String line = "";
		String tmp;
		while ((tmp = reader.readLine()) != null) {
			line += tmp;
		}
		reader.close();
		assertEquals(testString, line);
	}
```

### テスト中メソッド名取得
```java:JUnit4(抜粋)
	@Rule
	public TestName testName = new TestName();
	
	@Test
	public void testAboutMethodName() throws Exception {
		System.out.println(
			testName.getMethodName() +  
			" is started.");
	}
```

```java:JUnit5(抜粋)

	@Test
	public void testAboutMethodName(TestInfo testInfo)
		throws Exception {
		System.out.println(testInfo + " is started.");
	}
```

```java:JUnit4標準出力
testAboutMethodName is started.
```

```Java:JUnit5標準出力
DefaultTestInfo [
displayName = 'testAboutMethodName(TestInfo)', 
tags = [],
testClass = class com.example.demo.JUnit5Tests, 
testMethod = public void com.example.demo.JUnit5Tests.
MethodNameTest(org.junit.jupiter.api.TestInfo)
 throws java.lang.Exception] is started.
```
コードの量から引き出せる情報量の差が圧倒的。

### エラーが出ても全ケースを実施、同じテストケースを様々なパターンで繰り返し実施

※ケースをまとめて実施することに関連した機能のため、
両方をまとめて比較します。

```java:JUnit4(抜粋)
@RunWith(Parameterized.class)
public class JUnit4ParameterizedTests {

	@Rule
	public ErrorCollector errors = new ErrorCollector();

	@Parameters
	public static List data() {
		return Arrays.asList(
			// 4の階乗は24、5の階乗は120、
			// 期待値が異なることでテストはエラーとなる
			new Object[][] { 
				{ 0, 1 }, 
				{ 1, 1 }, 
				{ 2, 2 }, 
				{ 3, 6 }, 
				{ 4, 23 }, 
				{ 5, 121 }, 
				{ 6, 720 } 
			}
		);
	}

	private int input;
	private int expected;

	public JUnit4ParameterizedTests(int input, int expected) {
		this.input = input;
		this.expected = expected;
	}

	@Test
	public void testParameterizedFactorial() {
		errors.checkThat("Factorial of " + this.input, , calcurateFactorial(this.input), is(this.expected));
	}

	public static int calcurateFactorial(int n) {
		if (n >= 2) {
			return calcurateFactorial(n - 1) * n;
		} else {
			return 1;
		}
	}
}
```

```java:JUnit5(抜粋)
public class JUnit5ParameterizedTests {

	// 4の階乗は24、5の階乗は120、
	// 期待値が異なることでテストはエラーとなる
	@ParameterizedTest
	@CsvSource({
		"0, 1",
		"1, 1",
		"2, 2",
		"3, 6",
		"4, 23",
		"5, 121",
		"6, 720"
	})
	public void testParameterizedFactorial(int input, int expected) {
		int actual = input;
		assertEquals(calcurateFactorial(actual), expected);
	}

	public static int calcurateFactorial(int n) {
		if (n >= 2) {
			return calcurateFactorial(n - 1) * n;
		} else {
			return 1;
		}
	}
}
```

注意事項として、JUnit5ではテストメソッドに@Testをつけないこと。
つけてしまうと、次の警告メッセージが表示されます。
ただ、テストは実行できます。

```
This is typically the result of annotating a method 
with multiple competing annotations 
such as @Test, @RepeatedTest, 
@ParameterizedTest, @TestFactory, etc.
```

### 最後に
最後までお読みいただき、ありがとうございました。