【Android】JUnit5で複数の引数パターンに対しParameterized Testを行う

# 概要

複数の引数を取り、組み合わせに応じて返す結果が何パターンもあるとき、
1つ1つテストを書いているとコード量が多くなってしまいます。
そこでParametrised Testを使います。
引数のパターン数だけ用意すれば、一つのメソッドをそのパターン数分だけテストしてくれる優れものです。

Android-Kotlinの単体テストにはJUnit5を使います。

## JUnit5導入

build.gradle(トップレベル)

```gradle
dependencies {
        classpath "de.mannodermaus.gradle.plugins:android-junit5:1.4.2.0"
}
```

build.gradle(app)

```gradle
dependencies {
                testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
                testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
                testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2'
                testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}
```

### ※注意: 自動でImportされないかも

私がJUnit5を試したときにはなぜかAndroidStudioに認識されずハマりました。。
Importを手動でやるとうまくいきます。
[JUnit5の@ParameterizedTestがAndroidStudioで認識されない](https://qiita.com/ryuji-oda/items/5cb655c81186942cd598)

## テストを書く

REST APIを2つ叩いた結果を引数に加えて、使用可能な型リストを返すような関数:
`UseCase.getSelectableTypeList()` をテストします。

APIレスポンスはJSON形式で返ってくるのでダミーで作り、data classに変換します。

引数の組み合わせを`selectableTestCase`として定義しておき、テストで
 `@MethodSource("selectableTestCase")`を呼び出します。

テスト内で受け取った引数をdata classのvalueと差し替えて、
関数の返り値が期待通りか判定します。

```kotlin
import com.google.gson.Gson
import com.google.gson.JsonIOException
import com.google.gson.JsonSyntaxException
import jp.ucs.cardApp.domain.entity.auth.HogeResponse
import org.junit.jupiter.api.Assertions.*
import java.io.InputStreamReader
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.arguments
import org.junit.jupiter.params.provider.MethodSource
import java.util.stream.Stream


internal class HogeUseCase {
　　　// data class
    lateinit var hogeResponse: HogeResponse
    lateinit var fugaResponse: FugaResponse

    companion object {
        const val HOGE_JSON = "{\"id\":\"2\",\"id\":\"3k3k02k\",\"aaaIdFlg\":\"0\",\"hogeFlg\":\"0\",\"cccFlg\":\"0\",\"dddState\":\"0\"}"
        const val FUGA_JSON = "{\"beginingDate\":\"20211010\",\"endDate\":\"20210921\",\"hogeType\":\"2\",\"hogeNum\":\"0\",\"hogeAmount\":\"030\",\"fugaTypeFlg":\"0\",\"fugaAmount\":\"030\"}\n"

        @JvmStatic
        fun selectableTestCase(): Stream<Arguments> =
            Stream.of(
                // 引数の順番:fugaType id, hogeFlg, hogeType, fugaFlg, 期待値
                arguments(FugaType.BANANA, "2", "1", "1", "1", Type.A),
                arguments(FugaType.BANANA, "2", "0", "1", "1", Type.B),
                arguments(FugaType.BANANA, "2", "0", "2", "2", Type.C),
                arguments(FugaType.GORIRA, null, "0", "1", "1", Type.S),
                arguments(FugaType.GORIRA, null, "0", "2", "2", Type.G),
                arguments(FugaType.GORIRA, "1", "0", "1", "1", Type.S),
                arguments(FugaType.GORIRA, "1", "0", "2", "2", Type.ELSE),
                arguments(FugaType.GORIRA, "2", "0", "1", "1", Type.S),
                arguments(FugaType.GORIRA, "2", "0", "2", "2", Type.ELSE),
            )
    }

    @org.junit.jupiter.api.BeforeEach
    fun setUp() {
        // JSONをdata classにしておく
        try {
            hogeResponse = Gson().fromJson(
                HOGE_JSON,
                HogeResponse::class.java
            )
            fugaResponse = Gson().fromJson(
                FUGA_JSON,
                FugaResponse::class.java
            )
        } catch (e: JsonSyntaxException) {
            e.printStackTrace()
        } catch (e: JsonIOException) {
            e.printStackTrace()
        } finally {
        }
    }

// 予め定義しておいた引数たちをMethodSourceアノテーションで呼び出す
// 引数の数分このテストを回してくれる
    @ParameterizedTest
    @MethodSource("selectableTestCase")
    fun getSelectableTypeList(
        fugaType: FugaType,
        id: String?,
        hogeFlg: String,
        hogeType: String,
        fugaFlg: String,
        expectedValue: List<Type>
    ) {
        Pair(hogeResponse, fugaResponse).let {
            val actual = UseCase.getSelectableTypeList(
                fugaType,
                // ダミーのdata classの中身を差し替える
                it.first.copy(id = id, hogeFlg = hogeFlg),
                it.second.copy(hogeType = hogeType, fugaFlg = fugaFlg)
            )
            assertEquals(expectedValue, actual)
        }
    }
```