5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

お題

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

入力値 期待結果
z Z
test TEST
Abc ABC
null null
"" ""

このような「入力値」と「期待結果」のパターンがたくさんあるものを1テストケースずつ分けて書いていくのは、けっこうしんどいので、そういったものをまとめてテストできる”パラメータ化テスト”という方法がある。
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://github.com/sky0621/try-spock-java8

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

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

テスト対象のクラス

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のパラメータ化テストの書き方

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を使った書き方

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
    }

}

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

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

テスト対象のクラス

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を使った書き方

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を使うと誰でもひと目でわかるテストコードになるんじゃないかと思う。

参考

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?