【Android】TestParameterInjectorを使ってParameterizedテストを行おう

# はじめに
Googleが出している [TestParameterInjector](https://github.com/google/TestParameterInjector) を使うと、すこぶる簡単にParameterizedテスト（パラメータ化テスト）を書くことができたので、サンプルコードなどを載せつつ説明したいと思います。

# TestParameterInjectorに関して
詳しくは https://opensource.googleblog.com/2021/03/introducing-testparameterinjector.html のブログ記事を参考いただければと思いますが、簡単にまとめると…

- Googleが2021年3月に公開したJUnit4 Test Runner
- Googleのコードベースで新しいテストのために最も使用されているフレームワーク
  
>  TestParameterInjector is now the most used framework for new tests in the Google codebase:

という感じです。

使用する場合は

```gradle
testImplementation 'com.google.testparameterinjector:test-parameter-injector:1.3'
```

と `dependencies` に追加すればOKです。（ `1.3` は執筆時のバージョン）


# サンプルコード
サンプルコードを交えつつ、 `TestParameterInjector` を使うとどうなるのかを説明します。

## Piyo
テスト対象のコードとして `Piyo` クラスを用意しました。

`say` メソッドがあり、引数の `value` により

- 2で割り切れるときは「piyopiyo」
- それ以外のときは「piyo」

を返却します。

```kotlin
class Piyo {
    fun say(value: Int): String {
        return when {
            value % 2 == 0 -> "piyopiyo"
            else -> "piyo"
        }
    }
}
```

## PiyoTest

`Piyo` に対して **`TestParameterInjector` を使わないで** 素朴なテストコードを記述すると以下のようになるでしょうか。

- 2のときに「piyopiyo」であること
- 1, 3のときに「piyo」であること
- 0のときは「piyopiyo」であること

見ていただけるとわかりますが、パラメータの値（ `value` ）とAssertion時の値が違う以外はほぼ重複したコードになってしまっています。

```kotlin
class PiyoTest {

    @Test
    fun piyo_say_piyopiyo_when_2() {
        val value = 2 // ここと
        val piyo = Piyo()

        val result = piyo.say(value)

        Assert.assertEquals("piyopiyo", result) // ここが違うだけ
    }

    @Test
    fun piyo_say_piyo_when_1() {
        val value = 1
        val piyo = Piyo()

        val result = piyo.say(value)

        Assert.assertEquals("piyo", result)
    }

    @Test
    fun piyo_say_piyo_when_3() {
        val value = 3
        val piyo = Piyo()

        val result = piyo.say(value)

        Assert.assertEquals("piyo", result)
    }

    @Test
    fun piyo_say_piyopiyo_when_0() {
        val value = 0
        val piyo = Piyo()

        val result = piyo.say(value)

        Assert.assertEquals("piyopiyo", result)
    }
}
```

## TestParameterInjectorを使ってみる
`Piyo` に対して **`TestParameterInjector` を使って** パラメータ化テストを作成してみると以下のようになります。

```kotlin
@RunWith(TestParameterInjector::class) // ← ①
class PiyoTest2 {

    // ↓ ②
    enum class PiyoTestCase(val value: Int, val expected: String) {
        CASE0(0, "piyopiyo"),
        CASE1(1, "piyo"),
        CASE2(2, "piyopiyo"),
        CASE3(3, "piyo"),
    }

    // ↓ ③
    @Test
    fun piyo_say(@TestParameter piyoTestCase: PiyoTestCase) {
        val value = piyoTestCase.value // valueを使う
        val piyo = Piyo()

        val result = piyo.say(value)

        Assert.assertEquals(piyoTestCase.expected, result) // expectedを使う
    }
}
```

1. `@RunWith` で `TestParameterInjector` を指定する
2. `PiyoTestCase` で引数（ `value` ）と期待値（ `expected` ）の組み合わせを定義する
3. テストの引数に `@TestParameter` を指定した `PiyoTestCase` を指定する

こうしてやると

テストコード `piyo_say` の引数が
- `CASE0` の場合
- `CASE1` の場合
- `CASE2` の場合
- `CASE3` の場合

と4回呼ばれることになります。

↓実行した結果もこの通り、テストメソッドは1つなのに、4つのテスト結果が出力されます。
![スクリーンショット 2021-06-05 23.35.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/995/3744ba7b-81d2-c4e3-11ba-3c0868eef03e.png)


`TestParameterInjector` の適用前・後でテストコードがかなりスッキリしたことがおわかりになったかと思います。

# その他の方法

上記のサンプルコードではテストケースをenumで定義し、テストメソッドの引数に渡す方法でしたが、

- `Boolean` や `Int` を使う
- `@TestParameter` アノテーションを付けてフィールドに定義する

なども可能です。
フィールドに定義した場合は、テストコード全体でパラメータ化されて実行されることになります。

```kotlin
@RunWith(TestParameterInjector::class)
class MyTest {

    @TestParameter
    var isDryRun = false

    @TestParameter("1", "2", "3")
    var i = 0

    @Test
    fun test1(@TestParameter enableFlag: Boolean) {
        // 12通りの組み合わせで呼ばれる
        // test1[isDryRun=false,i=1,false]
        // test1[isDryRun=false,i=1,true]
        // test1[isDryRun=false,i=2,false]
        // test1[isDryRun=false,i=2,true]
        // test1[isDryRun=false,i=3,false]
        // test1[isDryRun=false,i=3,true]
        // test1[isDryRun=true,i=1,false]
        // test1[isDryRun=true,i=1,true]
        // test1[isDryRun=true,i=2,false]
        // test1[isDryRun=true,i=2,true]
        // test1[isDryRun=true,i=3,false]
        // test1[isDryRun=true,i=3,true]
    }

    @Test
    fun test2() {
        // 6通りの組み合わせで呼ばれる
        // test2[isDryRun=false,i=1]
        // test2[isDryRun=false,i=2]
        // test2[isDryRun=false,i=3]
        // test2[isDryRun=true,i=1]
        // test2[isDryRun=true,i=2]
        // test2[isDryRun=true,i=3]
    }
}
```

また **Advanced usage** として動的な `@TestParameter` の生成などもできるようなので、興味のある方は見てみても良いかもしれません。
https://github.com/google/TestParameterInjector#advanced-usage

# 終わりに
`TestParameterInjector` の簡単な説明とサンプルコードをご紹介しました。
テストコードが驚くほどスッキリするので、使えるところでは積極的に使って、楽しくテストコードを書いていきたいところです。