JUnit5: ParameterizedTest+ArgumentSetでテスト出力を改善

# JUnit 5.11の新機能 - ArgumentSetとは？

JUnit5では、ParameterizedTestを使用することで入力・出力の単純な繰り返しを単純化することができるようになりました。

しかし、5.10までは名前を付けながら複数の引数を設定することが難しかったため、例えば[1]テストケース名[2]インプット[3]期待値となるような書き方をするテストケースの表示はあまり改善できていませんでした。

5.11以降は、`ArgumentSet`を使用することで入力・出力の単純な繰り返しがさらに改善されます。

https://junit.org/junit5/docs/5.11.3/user-guide/index.html#writing-tests-parameterized-tests-display-names

# 改善例

今回の例は、nablarch-exmample-webの`ProjectProfitTest`を例に改善例を示します。

https://github.com/nablarch/nablarch-example-web/blob/main/src/test/java/com/nablarch/example/app/web/action/ProjectProfitTest.java

## 改善前

```java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

    @Nested
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    class 配賦前利益 {
        List<Param<Long>> parameters() {
            return Arrays.asList(
                    new Param<>(new ProjectProfit(987654321, 123456789, 12345678, 300), 851851854L),
                    new Param<>(new ProjectProfit(999999999, 0, 0, 300), 999999999L),
                    new Param<>(new ProjectProfit(0, 999999999, 999999999, 300), -1999999998L),
                    new Param<>(new ProjectProfit(0, 0, 0, 300), 0L),
                    new Param<Long>(new ProjectProfit(null, 2000000, 1000000, 300), null),
                    new Param<Long>(new ProjectProfit(3000000, null, 1000000, 300), null),
                    new Param<Long>(new ProjectProfit(3000000, 2000000, null, 300), null),
                    new Param<>(new ProjectProfit(3000000, 2000000, 1000000, null), 0L),
                    new Param<Long>(new ProjectProfit(null, null, null, 300), null)
            );
        }

        @ParameterizedTest
        @MethodSource("parameters")
        void test(Param<Long> param) throws Exception {
            assertThat(param.projectProfit.getProfitBeforeAllocation(), is(param.expected));
        }
    }
    
    private static class Param<T> {

        private ProjectProfit projectProfit;

        private T expected;

        public Param(final ProjectProfit projectProfit, final T expected) {
            this.projectProfit = projectProfit;
            this.expected = expected;
        }
    }
```
### 出力

- 改善前のテストは、下記の点でやや読みづらい
  1. どのようなケースかの説明がない。`ProjectProfitTest$Param@...`部分の情報には意味がない
  1. 期待値が表示されない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/b9d9830d-e86e-c372-536b-242be9a63fa4.png)


## 改善後

```java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

    static List<Arguments.ArgumentSet> caseGetGrossProfit() {
        return List.of(
                caseOf("正常値", new ProjectProfit(987654321, 123456789, 12345678, 300), 851851854L),
                caseOf("売上高上限値", new ProjectProfit(999999999, 0, 0, 300), 999999999L),
                caseOf("売上原価・販管費上限値", new ProjectProfit(0, 999999999, 999999999, 300), -1999999998L),
                caseOf("売上高・売上原価・販管費0", new ProjectProfit(0, 0, 0, 300), 0L),
                caseOf("売上高null", new ProjectProfit(null, 2000000, 1000000, 300), null),
                caseOf("売上原価null", new ProjectProfit(3000000, null, 1000000, 300), null),
                caseOf("販管費null", new ProjectProfit(3000000, 2000000, null, 300), null),
                caseOf("本社配賦null", new ProjectProfit(3000000, 2000000, 1000000, null), 0L),
                caseOf("売上高・売上原価・販管費null", new ProjectProfit(null, null, null, 300), null)
        );
    }
    
    @DisplayName("配賦前利益")
    @ParameterizedTest(name = "[{index}] {argumentSetNameOrArgumentsWithNames} - 期待値: {1}")
    @MethodSource("caseGetGrossProfit")
    public void testGetGrossProfit(ProjectProfit projectProfit, Long expected) throws Exception {
        assertThat(projectProfit.getProfitBeforeAllocation(), is(expected));
    }
    
    private static<E> Arguments.ArgumentSet caseOf(String name, ProjectProfit projectProfit, E expected){
        return argumentSet(name, projectProfit, expected);
    }
```

### 出力

- 改善後のテストは、下記の点が改善されている
  1. どのようなケースかの説明が表示される
  1. 期待値が表示される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161516/e0ad976f-ee16-8a12-6978-4cd5bfd7d5f8.png)

# 参考資料

## (JUnitリリースノート)New Features and Improvements

https://junit.org/junit5/docs/5.11.3/release-notes/#release-notes-5.11.0-junit-jupiter-new-features-and-improvements

## (JUnitユーザーガイド)2.17.6. Customizing Display Names

https://junit.org/junit5/docs/5.11.3/user-guide/index.html#writing-tests-parameterized-tests-display-names

## (javadoc)org.junit.jupiter.params.provider.Arguments#argumentSet

https://junit.org/junit5/docs/5.11.3/api/org.junit.jupiter.params/org/junit/jupiter/params/provider/Arguments.html#argumentSet(java.lang.String,java.lang.Object...)

## Github 

https://github.com/junit-team/junit5/pull/3825/files