PHPUnitのtestWithアノテーションを使ってみる


プロジェクトのPHPUnitが古く、バージョンを上げようと思った。
最近の変更点を見ていたら`testWith`アノテーションというのが4.8から増えたことに気づく。
便利そうなので使おうと思ったががパッと見でドキュメントが見当たらなかったのでメモを残す。

（2018/07/31追記）　
いつのまにかドキュメントが増えていた。
[2. アノテーション@testWith](http://phpunit.readthedocs.io/ja/latest/annotations.html#testwith)

# 環境

```
Installing phpunit/phpunit (5.7.8)

PHP 7.1.0 (cli) (built: Jan  3 2017 15:32:50) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.1.0-dev, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.1.0, Copyright (c) 1999-2016, by Zend Technologies
    with Xdebug v2.5.0, Copyright (c) 2002-2016, by Derick Rethans
```

# 結論

json形式で指定しましょう。
連想配列だけちょっと特殊だから気をつける。

# とりあえず使ってみる

## 数値

```php
    /**
     * @testWith [1, 2, 3]
     *           [1.1, 2.2, 3.3]
     */
    public function testNum($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }
```

## 文字列

```php
    /**
     * @testWith ["a", "b", "ab"]
     */
    public function testStr($a, $b, $expected)
    {
        $this->assertEquals($expected, $a . $b);
    }
```

## 配列

```php
    /**
     * @testWith [["a"], ["b"], ["a","b"]]
     */
    public function testAry($a, $b, $expected)
    {
        $this->assertEquals($expected, array_merge($a, $b));
    }
```

ここまでは想定内。


## 連想配列

```php
    /**
     * @testWith [["a"=>1], ["b"=>2], ["a"=>1,"b"=>2]]
     */
    public function testHash($a, $b, $expected)
    {
        $this->assertEquals($expected, array_merge($a, $b));
    }
```

配列までのノリで出来るかと思ったらテスト始まらなかったので指定の仕方おかしいんだろうということでソースを確認。

```php:Test.php#L511
if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
    $offset            = strlen($matches[0][0]) + $matches[0][1];
    $annotationContent = substr($docComment, $offset);
    $data              = [];
    foreach (explode("\n", $annotationContent) as $candidateRow) {
        $candidateRow = trim($candidateRow);
        if ($candidateRow[0] !== '[') {
            break;
        }
        $dataSet = json_decode($candidateRow, true);
        if (json_last_error() != JSON_ERROR_NONE) {
            throw new PHPUnit_Framework_Exception(
                'The dataset for the @testWith annotation cannot be parsed: ' . json_last_error_msg()
            );
        }
        $data[] = $dataSet;
    }
    if (!$data) {
        throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
    }
    return $data;
}
```

引用元：
https://github.com/sebastianbergmann/phpunit/blob/5.7/src/Util/Test.php#L511


うん、めっちゃ`json_decode`している。
よって連想配列を指定したい場合は下記のようになる。

```php
    /**
     * @testWith [{"a": 1}, {"b": 2}, {"a": 1, "b": 2}]
     */
    public function testHash($a, $b, $expected)
    {
        $this->assertEquals($expected, array_merge($a, $b));
    }
```

# まとめ

一通り指定は出来るのでよっぽど無い限り簡単なテストはtestWithアノテーションでいいのかなと思ったり。
色々やりたいぞいってなったら普段どおりdataProviderアノテーションで頑張る感じで！

[動くサンプル](https://gist.github.com/hrs-o/ce935925fb273e33d7005c4d46191831)

# 参考
[PHPUnit の各バージョンについて調べてみた](http://qiita.com/waterada/items/08595e7ab849f7c09903)
[Annotation @testWith not documented](https://github.com/sebastianbergmann/phpunit-documentation/issues/358)

~~2018/03/13 ドキュメントが追加されてることに気付いたが連想配列周りの罠に関しては未記載っぽい~~
~~[2. アノテーション@testWith](http://phpunit.readthedocs.io/ja/latest/annotations.html#testwith)~~