📝

PHPUnit:パラメタライズドテスト

2020/10/05に公開

パラメタライズドテストとは

引数を切り替えて同じテストを繰り返し実行するテストのこと。
FizzBuzzを例にみてみましょう。

FizzBuzz

1から100までの数字を出力する。ただし
3の倍数は"Fizz"
5の倍数は"Buzz"
3と5の倍数は"FizzBuzz"と出力する

実装はこんな感じですね。

final class FizzBuzz
{
    /**
     * @param int $number
     * @return string
     */
    public function convert(int $number) : string
    {
        if ($number % 15 == 0) {
            return 'FizzBuzz';
        }
        if ($number % 3 == 0) {
            return 'Fizz';
        }
        if ($number % 5 == 0) {
            return 'Buzz';
        }
        return (string)$number;
    }
}

一旦テスト書いてみる

use PHPUnit\Framework\TestCase;

class FizzBuzzTest extends TestCase
{
    /**
     * @noinspection NonAsciiCharacters
     */
    public function test_3の倍数でFizzになる()
    {
        $fizz_buzz = new FizzBuzz();
        $this->assertEquals("Fizz", $fizz_buzz->convert(3));
    }
}

ひとまず3についてはテストできました。
ただこれだけだと、他の3の倍数が正常に動作するか不安が残ります。
とはいえ、全ての値をチェックするのも余計な気がします。

最小の倍数:3
次に小さい倍数:6
範囲内の最大の倍数:99

このあたりを検証できれば十分かと思います。

愚直に検証してみる

use PHPUnit\Framework\TestCase;

class FizzBuzzTest extends TestCase
{
    /**
     * @noinspection NonAsciiCharacters
     */
    public function test_3の倍数でFizzになる()
    {
        $fizz_buzz = new FizzBuzz();
        $this->assertEquals("Fizz", $fizz_buzz->convert(3));
        $this->assertEquals("Fizz", $fizz_buzz->convert(6));
        $this->assertEquals("Fizz", $fizz_buzz->convert(99));
    }
}

愚直に書くとこのようなテストになりますが

$ ./vendor/bin/phpunit --debug
PHPUnit 9.3.11 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.4.10
Configuration: /home/dzn/workspace/phpunit_sample/phpunit.xml

Test 'FizzBuzzTest::test_3の倍数でFizzになる' started
Test 'FizzBuzzTest::test_3の倍数でFizzになる' ended


Time: 00:00.002, Memory: 4.00 MB

OK (1 test, 3 assertions)

1 test, 3 assertions と正常にテストできています。
ただこの書き方だと下記の懸念があります。

1. テストコードが見づらい

今回は3つほどなので、あまり多くはありませんが検証したい項目が増えるとテストとして可読性が悪くなり、何を検証したいのかも見えづらくなります。

2. アサーションルーレット

アサーションは3つありますが、テストが失敗した場合、単純に失敗したテストは1つでありどこのアサーションでこけたのかがわかりづらくなります(アサーションルーレットと呼ばれています)
なるべく1テスト1アサーションを心掛けたいものです。

@dataProvider

dataProviderというアノテーションを使用すれば、すっきり記述することができます。

class FizzBuzzTest extends TestCase
{
    public function fizzPattern()
    {
        return [
            "範囲中の最小の3の倍数" => [3],
            "次に小さい3の倍数" => [6],
            "範囲中の最大の3の倍数" => [99]
        ];
    }

    /**
     * @dataProvider fizzPattern
     * @noinspection NonAsciiCharacters
     */
    public function test_3の倍数でFizzになる($number)
    {
        $fizz_buzz = new FizzBuzz();
        $this->assertEquals("Fizz", $fizz_buzz->convert($number));
    }
}

@dataProviderにfizzPatternを指定しています。
これはテスト実行するためのパラメータを取得するfunctionを指定しています。
returnした配列の数分、テストが繰り返し実行されます。

今回でいうと、$numberに3,6,99の値が渡され3回実行されることになります。

$ ./vendor/bin/phpunit --debug
PHPUnit 9.3.11 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.4.10
Configuration: /home/dzn/workspace/phpunit_sample/phpunit.xml

Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "範囲中の最小の3の倍数" (3)' started
Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "範囲中の最小の3の倍数" (3)' ended
Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "次に小さい3の倍数" (6)' started
Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "次に小さい3の倍数" (6)' ended
Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "範囲中の最大の3の倍数" (99)' started
Test 'FizzBuzzTest::test_3の倍数でFizzになる with data set "範囲中の最大の3の倍数" (99)' ended


Time: 00:00.003, Memory: 4.00 MB

OK (3 tests, 3 assertions)

キーの名称はなんでもよいので、データの意図を書いておくと、テスト上にも表示されこけたときもわかりやすいです。

以上、パラメタライズドテストのやり方でした。

参考

公式ドキュメント

Discussion