Laravel5.5 Seederで単体テストのテストデータを登録してみる

# 概要
Laravel5.2の時にやってたテストデータの登録方法が上手くいかず、少しハマったのでそのまとめ。

# 環境
Laravel 5.5.9
PHP 7.1.3
MySQL 5.7.19

# Laravel5.2の時の方法
確かこんな感じで、@beforeアノテーションでテスト前にSeederを実行してました。

```php:tests\Unit\ExampleTest.php
<?php

namespace Tests\Unit;

use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    /**
     * @before
     */
    public function setUpFixtures()
    {
        $this->seed(__CLASS__ . 'Seeder');
    }

    /**
     * @test
     */
    public function サンプルテスト()
    {
        $this->markTestIncomplete('テストを実装する！');
    }
}

/**
 * テストデータ登録Seeder
 */
class ExampleTestSeeder extends Seeder
{
    public function run()
    {
        // @todo テストデータ登録
    }
}

```

# Laravel5.5でも同様にやってみる
RefreshDatabaseトレイトが追加されてるので、ついでに変更。

```php:tests\Unit\ExampleTest.php
<?php

namespace Tests\Unit;

use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @before
     */
    public function setUpFixtures()
    {
        $this->seed(__CLASS__ . 'Seeder');
    }

    /**
     * @test
     */
    public function サンプルテスト()
    {
        $this->markTestIncomplete('テストを実装する！');
    }
}

/**
 * テストデータ登録Seeder
 */
class ExampleTestSeeder extends Seeder
{
    public function run()
    {
        // @todo テストデータ登録
    }
}

```

## 結果
エラーが発生。
どうもアプリケーションクラスの初期化が終わってないっぽい。
~~アプリケーションの初期化の方法が変わったのかな？（後で調べて追記するかも）~~

```
1) Tests\Unit\ExampleTest::サンプルテスト
Error: Call to a member function call() on null
```


---
（追記：2017-09-22）
PHPUnit側の処理が変わったみたい。（PHPUnit\Util\Test::getHookMethodsメソッド）
（PHPUnitのバージョンによって挙動が変わるなら、サンプルソースも問題なので修正しました）

- Laravel5.2で使っていた時のバージョン：4.8.31
    - @beforeClass・@before・@after・@afterClass 全て、見つかった順で変数へ格納
- Laravel5.5で使っているバージョン：6.3.0
    - @beforeClass・@beforeはarray_unshiftで変数へ格納、@after・@afterClass は見つかった順で変数へ格納

---


## 原因
ソースをみた感じだと、setUpメソッド内でアプリケーションの初期化を行なっているが、PHPUnit的にはsetUpより@beforeアノテーションで定義したものが先に呼ばれる模様。
（~~array_shift~~ array_unshiftで、見つかった@beforeのメソッドを格納してるので、後に見つかったものが先に呼ばれるっぽい。そしてデフォルト値にsetUpが入っている。）

#　Laravel5.5で動くよう修正
afterApplicationCreatedメソッドでアプリケーションの初期化完了時のコールバックを定義できるみたい。

```php:tests\Unit\ExampleTest.php
<?php

namespace Tests\Unit;

use Illuminate\Database\Seeder;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @before
     */
    public function setUpFixtures()
    {
        // 下のように書き換え
        //$this->seed(__CLASS__ . 'Seeder');

        // 訂正（2017-09-22）
        //$this->afterApplicationCreated(function() {
        //    $this->seed(__CLASS__ . 'Seeder');
        //});
        if ($this->setUpHasRun) {
            // アプリケーションのセットアップが完了しているならそのまま実行
            $this->seed(__CLASS__ . 'Seeder');
        } else {
            // アプリケーションのセットアップが完了していないならコールバックで設定
            $this->afterApplicationCreated(function() {
                $this->seed(__CLASS__ . 'Seeder');
            });
        }
    }

    /**
     * @test
     */
    public function サンプルテスト()
    {
        $this->markTestIncomplete('テストを実装する！');
    }
}

/**
 * テストデータ登録Seeder
 */
class ExampleTestSeeder extends Seeder
{
    public function run()
    {
        // @todo テストデータ登録
    }
}

```

再度実行して動くことを確認。

```cmd
$ vendor/bin/phpunit 
PHPUnit 6.3.0 by Sebastian Bergmann and contributors.

I                                                                   1 / 1 (100%)

Time: 580 ms, Memory: 12.00MB

OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Incomplete: 1.
```

# まとめ
~~setUp() > @before > テスト実行　の順だと思い込んでた...~~
（2017-09-22）
追記しましたが、PHPUnitのバージョンで挙動が変わるっぽい。
ちゃんと確認してよかった。。サンプルソースも修正しました。

内容に間違いがあるとか、もっと良い方法があるとか、何かあればご指摘ください。

以上です。