Fakerで自動テスト用のダミーデータ作成を自動化する

データベースを含んだ自動テストは面倒ですね。なにが面倒って、テスト用のデータ、いわゆるフィクスチャを準備するのがもう。

Yii はデータベースの結果をモックする純粋なロジックテストではなく、ファンクショナルテストやデータベース込みのテストを指向しています。2.0 になって、BDDふうのテスティングフレームワーク [Codeception](http://codeception.com/) のテストプロジェクトが一式ついてくるようになりました。BDD ふうにページをテストするにはホンモノが動くことが前提、つまりデータベースも当然要るわけです。

フィクスチャはすべて手書きしてもいいのですが、たとえば 21 件以上でページネーションすることを試そうと思ったら...

というわけで [Faker](https://github.com/fzaninotto/Faker) を活用しましょう。

Yii のプロジェクトテンプレートの `tests` フォルダ以下には、テスト環境専用の `codeception\bin\yii` コマンドが置いてあり、こんな感じで、フィクスチャのサポートが追加で組み込まれています。

```php
$config = yii\helpers\ArrayHelper::merge(
    // ... ,
    [
        'controllerMap' => [
            'fixture' => [
                'class' => 'yii\faker\FixtureController',
                'fixtureDataPath' => '@tests/codeception/fixtures/data',
                'templatePath' => '@tests/codeception/templates',
                'namespace' => 'tests\codeception\fixtures',
            ],
        ],
    ]
);
```

設定にしたがって `tests/codeception/templates` にフィクスチャのテンプレートを書きます。たとえばユーザー用のフィクスチャなら `templates/user.php` にこう書きます。

```php
<?php
/**
 * @var $faker \Faker\Generator
 * @var $index integer
 */
return [
    'email' => $faker->email,
    'password_hash' => Yii::$app->security->generatePasswordHash('password_' . $index),
    'auth_key' => Yii::$app->security->generateRandomString(),
    'name' => $faker->firstName . ' ' . $faker->lastName,
    'birthday' => $faker->date(),
    'created_at' => $faker->unixTime,
    'updated_at' => $faker->unixTime,
];
```

`$faker` からプロパティを取ると、取り出すたびにデタラメなそれらしいデータが出てきます。ランダムだと困るものは、 `$index` を使うと計算で出したり決まった配列から取り出したりできます。

で、 `tests/codeception/bin/yii fixture/generate user` を実行するとテンプレートのパスとデータのパスが設定してあるので...

```
$ tests/codeception/bin/yii fixture/generate user
Fixtures will be generated under the path: 
        /.../tests/codeception/fixtures/data

Templates will be taken from path: 
        /.../tests/codeception/templates

        * user
Generate above fixtures? (yes|no) [no]: yes
The following fixtures template files were generated:

        * user
```

はい、これでできました。じゃーん!

```php
<?php

return [
    [
        'email' => 'pauline22@weber.info',
        'password_hash' => '$2y$13$kU/MfgI/XUD6JacW7VFe2O3iab3pksTEvrXaJ9c0mof86lnNEJwW.',
        'auth_key' => 'l7iv336Q00OrP0Mcn7sUgvbUAhd3gU1W',
        'name' => 'Ophelia Jacobs',
        'birthday' => '1980-09-09',
        'created_at' => 602074437,
        'updated_at' => 390537715,
    ],
    [
        'email' => 'kacie78@yahoo.com',
        'password_hash' => '$2y$13$ry7b0lKIcY1grUQmQBkjgupbRsNxAnoOsAgOwJsxgXQCnlljYM3W6',
        'auth_key' => 'Ux5iYaPTpY5pgym9fuwEjrZXOCdZbXO5',
        'name' => 'Audra Cole',
        'birthday' => '2010-05-17',
        'created_at' => 447193310,
        'updated_at' => 293260057,
    ],
];
```

2個じゃ足りないなら、

```
tests/codeception/bin/yii fixture/generate user --count=10
```

で、こういうのが 10 件でも 20 件でも作れます。

本当にバラバラなデータを手で作るのは面倒だし、人間が手でやるとどうしても恣意的になってしまいます。思い込みでテストケースの漏れが出てくることもあります。

テンプレートから生成することのメリットとして、再現性のないパスワードハッシュを固定できるというのもあります。フィクスチャデータで `generatePasswordHash` を呼ぶと、セキュリティの都合で実際には毎回違うデータになってしまいます。細かいことかもしれませんが、フィクスチャデータで値を生成すると、場合によっては失敗を再現できないかもしれません。フィクスチャデータはテストコードの一部です。

というわけで、テンプレートがあるからといって、フィクスチャデータをバージョン管理しないというのはナシです。テストがグリーンでコミットしたことを確実に示すために、フィクスチャはバージョン管理しましょう。

バージョン管理するということは、もうこのデータは開発者のものです。生成し直すまで、好きなように編集して、都合の悪いところは適当に書き換えましょう。多対多テーブルなんかは、テンプレートを書かずにフィクスチャを直接書いてもいいかもしれません。

フィクスチャデータを使うには、クラスを定義しなければなりません。といっても、`AssetBundle` 並みに非常に簡単な実装です。テスト用 `yii`
 コマンドの `FixtureController` の `namespace` が指すところに...

```php
<?php
namespace tests\codeception\fixtures;

use yii\test\ActiveFixture;

class UserFixture extends ActiveFixture
{
    public $modelClass = 'app\models\User';
    public $depends = [];
} 
```

`$modelClass` はどのテーブルにロードするかという情報を引くための ActiveRecord クラスです。

`$depends` には、このフィクスチャをロードする前にロードして欲しい他のフィクスチャのクラス名を複数登録します。まさに `AssetBundle` のノリですね。これで、ロード時に外部キーの先がないなんてこともありません。

これでデータをロードすることができるようになりました。自動テストを実行する前に、作ったデータが実際にデータベース入るかどうかを確認しておきます。テスト用のデータベースをマイグレーションして、そこにすべてのフィクスチャを流し込みます。

```
tests/codeception/bin/yii migrate/up
tests/codeception/bin/yii fixture/load "*"
```

成功したらOKです。外部キー制約や文字数制限などの都合で入らないフィクスチャを作っていると、テストでも使うことができません。確認し終わったらアンロードします。

```
tests/codeception/bin/yii fixture/unload "*"
```

さて、ここまでやってもらえればもうあとはモチベーションを下げるヤツはいません。自動テストをサクサク書いていきましょう。

時間もないので、各種のテストでどうやってロードするかは、マニュアルを参照したり、アプリケーションテンプレートから読み取ったりしてください。

なお、Faker は一般的な道具なので、Laravel でも Symfony でも使えます。Packagist には CakePHP3 のプラグインも見かけました。

Laravel で Faker を使う場合は Jeff の記事を参照して下さい。

http://codebyjeff.com/blog/2013/09/quick-tip-faker-and-laravel-seed-generators

メリー・クリスマス