PHPUnitのデータプロバイダを使ってみる

# はじめに
PHPUnitでデータプロバイダについて学んだことをまとめます。
PHPのバージョンは7.3.11、
PHPUnitのバージョンは9.5.4です。

# データプロバイダを使わないテスト
まず、下記のFizzBuzzクラスのhandleFizzBuzz()についてのテストについて考えます。
一応処理内容は、引数の数字が
3で割り切れるなら Fizz を
5で割り切れるなら Buzz を
3で割り切れるかつ５で割り切れるなら FizzBuzz を
それ以外の数字の場合は、その数値を返します。

```PHP:FizzBuzz.php
class FizzBuzz
{
    public function handle(): void
    {
        for ($i = 0; $i < 100; $i++) {
            $this->handleFizzBuzz($i);
        }
    }

    public function handleFizzBuzz(int $number): string
    {
        if ($number% 3 === 0 && $number % 5 === 0) {
            return 'FizzBuzz';
        } elseif ($number % 3 === 0) {
            return 'Fizz'; 
        } elseif ($number % 5 === 0) {
            return 'Buzz';
        } else {
            return strval($number);
        }
    }
}
```

このhandleFizzBuzz()を単純にテストしようとすると、このように期待する結果ごと（今回だと4種類）にテストを書く必要があります。

```PHP:FizzBuzzTest
class FizzBuzzTest extends TestCase
{
    private FizzBuzz;

    public function setUp(): void
    {
        parent::setUp();
        $this->fizz_buzz = new FizzBuzz();
    }

    public function testGetNumber(): void
    {
        $input = 1;
        $expected = '1';
        $result = $this->fizz_buzz->handleFizzBuzz($input);
        $this->assertSame($result, $expected);
    }

    public function testGetFizz(): void
    {
        $input = 3;
        $expected = 'Fizz';
        $result = $this->fizz_buzz->handleFizzBuzz($input);
        $this->assertSame($result, $expected);
    }

    public function testGetBuzz(): void
    {
        $input = 5;
        $expected = 'Buzz';
        $result = $this->fizz_buzz->handleFizzBuzz($input);
        $this->assertSame($result, $expected);
    }

    public function testGetFizzBuzz(): void
    {
        $input = 15;
        $expected = 'FizzBuzz';
        $result = $this->fizz_buzz->handleFizzBuzz($input);
        $this->assertSame($result, $expected);
    }
}

```

このように、入力値と結果が異なるだけでその他の処理に関しては同じコードを書くのは好ましくありません。
これを解決するのが**データプロバイダ**です。

# データプロバイダを使ってみる
実際に先程のhandleFizzBuzz()のテストをデータプロバイダを使って書いてみます。

```PHP
class FizzBuzzTest extends TestCase
{
    public function setUp(): void
    {
        parent::setUp();
        $this->fizz_buzz = new FizzBuzz();
    }

    public function fizzBuzzProvider: array
    {
        return [
            [1, '1'],
            [3, 'Fizz'],
            [5, 'Buzz'],
            [15, 'FizzBuzz']
        ]
    }

    /**
    * @dataProvider fizzBuzzProvider
    */
    public function testHandleFizzBuzz($input, $expected): void
    {
        $result = $fizz_buzz->handleFizzBuzz($input);
        $this->assertSame($result, $expected);
    }
}
```

...
データプロバイダを使うとこれだけで済みます。
ポイントを一つずつ見ていきます。

### データプロバイダメソッドを作成する
まず、データプロバイダメソッドをPublicで作成します。
今回の例でいうと`fizzBuzzProvider()`です。

このデータプロバイダメソッドで、テスト対象メソッド(今回だとhandleFizzBuzz())の引数とそれの期待値を配列にし、それをテストパターン分まとめた二次元配列（またはオブジェクト）を返すようにします。
**※ループ処理の際に、引数と期待値の配列になっている必要があります。**
今回の場合は引数が1つですが、複数ある場合も配列の最後の要素を期待値にすればよいです。

```PHP:引数が複数の場合
public function testProvider(): array
{
    return [
       1, 2, true  // 引き数は1,1で期待値はtrue
    ]
}  

/**
* @dataProvider testProvider
*/
public function testHogeFunction($a, $b, $expected): void
{
    $result = hogeFunction($a, $b);
    $this->assertSame();
}
```

### テストメソッドを作成する
次に、テストメソッドを作成します。
このとき使用するデータプロバイダメソッドを`@dataProvider`アノテーションで指定してあげることで、**テストメソッドの引き数にデータプロバイダメソッドの返り値の要素が格納され、実行されます**。

なお、`@dataProvider`アノテーションでは名前空間も使用することができ、データプロバイダメソッドとテストメソッドは同じクラスや名前空間である必要はありません。
データプロバイダ用の名前空間を作成することも可能です。

```PHP:dataProviderアノテーションで名前空間を指定する
/**
* @dataProvider Tests\Unit\DataProviders\Hoge::dataProvider
*/
public function test($input, $expected): void
{
...
...
```

### データプロバイダメソッドをリファクタする
テストパターンが大量にある場合、データプロバイダメソッドの返り値の要素がそれぞれ何のテストデータか分かりにくくなってしまうため、キーに名前をつけておくと分かりやすくなります。

```PHP:
public function testProvider(): array
{
    return [
       'one is less than two'     => [1, 2, true]
       'two is not less than one' => [2, 1, false]
    ]
}  
```

# おわり
とりあえず、PHPUnitでデータプロバイダを使えるようになりましたがまだ基礎の部分なので、複雑なテストも書いてみたいです。

# 参考
[2. PHPUnit 用のテストの書き方](https://phpunit.readthedocs.io/ja/latest/writing-tests-for-phpunit.html)