Spockを使ったパラメータ化テスト

# お題
テストコードを書いている時に、（例えば、入力値を全て大文字にして返す関数に対して）下記のようなケースを用意したとする。

| 入力値 | 期待結果 |
| :---    | :---  |
| z | Z |
| test | TEST |
| Abc | ABC |
| null | null |
| "" | "" |
| あ | あ |

このような「入力値」と「期待結果」のパターンがたくさんあるものを１テストケースずつ分けて書いていくのは、けっこうしんどいので、そういったものをまとめてテストできる”パラメータ化テスト”という方法がある。
JavaのテストでおなじみのJUnitでもパラメータ化テストを書く専用の書き方があるのだけど、ちょっと複雑なパターンになると可読性が落ちるのと、テストの途中で失敗した時に、どこで失敗したのかがとても追いづらい。
そんな問題をSpockが見事に解決してくれるので、紹介してみる。
（Spock自体の登場は何年も前のことなので大分使い古しのツールだけど、いまだにこの手のテストでは強力なものとも言える）

# 開発環境
### # OS
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
```

### # Java
```
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
```
### # IDE
みんな大好き[IntelliJ IDEA](https://www.jetbrains.com/idea/)

# 実践

今回のソースは↓
https://github.com/sky0621/try-spock-java8

必要な依存ライブラリは「JUnit4」と「Spock」だけ。
https://github.com/sky0621/try-spock-java8/blob/master/try-spock-java8/pom.xml

## ■あるコード値を検証するメソッドのテストケースを作成

### テスト対象のクラス

```java
package com.example;

public class SomeCode {

    private String code;

    public SomeCode(String code) {
        this.code = code;
    }

    public boolean validate() {
        if (this.code == null) {
            return true;
        }
        if (this.code.equals("")) {
            return true;
        }
        if (this.code.length() != 3) {
            return false;
        }
        if (!this.code.startsWith("C")) {
            return false;
        }
        return true;
    }
}
```

### JUnit4のパラメータ化テストの書き方

```java
package com.example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.Collection;

import static org.junit.Assert.assertEquals;

@RunWith(Parameterized.class)
public class SomeCodeJUnitTest {

    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
                // コード、バリデーション結果
                {null, true},
                {"", true},
                {"C01", true},
                {"C001", false},
                {"A01", false},
        });
    }

    private String コード;
    private boolean バリデーション結果;

    public SomeCodeJUnitTest(String コード, boolean バリデーション結果) {
        this.コード = コード;
        this.バリデーション結果 = バリデーション結果;
    }

    @Test
    public void 与えられたコードに対するバリデーション結果が返る() {
        assertEquals(this.バリデーション結果, new SomeCode(this.コード).validate());
    }

}
```

まあ、このくらいの分量なら、まだ見れる。

### Spockを使った書き方

```groovy
package com.example

import spock.lang.Specification

class SomeCodeTest extends Specification {

    def "与えられたコードに対するバリデーション結果が返る"() {
        expect:
        new SomeCode(コード).validate() == バリデーション結果

        where:
        コード   | バリデーション結果
        null   | true
        ""     | true
        "C01"  | true
        "C001" | false
        "A01"  | false
    }

}
```

正直、初見で説明受けなくても何をテストしたいかわかる。

## ■与えられた文字列配列の中から指定のワードを含むもののみ抽出するメソッドのテストケースを作成

### テスト対象のクラス

```java
package com.example;

import java.util.List;

public class PgLang {

    public static String[] findMatchWords(List<String> pgList, String searchWord) {
        if (pgList == null) {
            return null;
        }
        if (searchWord == null) {
            return null;
        }
        return pgList.stream().filter(pg -> pg.contains(searchWord)).toArray(String[]::new);
    }

}
```

JUnit4のテストケースは省略。

### Spockを使った書き方

```groovy
package com.example

import spock.lang.Specification

class PgLangTest extends Specification {

    def "与えられたプログラミング言語の中から探索ワードを含む言語のみ返す"() {
        expect:
        PgLang.findMatchWords(プログラミング言語群, 探索ワード) == 結果群

        where:
        プログラミング言語群              | 探索ワード  | 結果群
        ["Java", "C++", "Go", "C"]    | "C"      | ["C++", "C"]
        ["Ruby", "Elixir", "Python"]  | "y"      | ["Ruby", "Python"]
        ["Scala", "Groovy", "Kotlin"] | "oo"     | ["Groovy"]
        null                          | "J"      | null
        ["C#", "JavaScript"]          | null     | null
        ["PHP", "Closure", "Lisp"]    | ""       | ["PHP", "Closure", "Lisp"]
    }

}
```

# まとめ
テストケースが多くなってくると辛くなってくるパラメータ化テストについては、Spockを使うと誰でもひと目でわかるテストコードになるんじゃないかと思う。

# 参考
http://spockframework.org/spock/docs/1.2/index.html