一連のphpunitの中で、1度だけmigrationを実行する

# 一連のphpunitの中で、1度だけmigrationを実行する

DatabaseMigrations.phpは使用すると処理時間が長くなってしまうので自前で処理を組む。
**詳細な理由は[こちら](#公式に載っている方法だと、各テストfunction毎にmigration:rollbackからのmigrationが実行され結構時間がかかってしまう)**

キチンと処理時間を計測して比較したわけではないですが、だいぶ早くなった感じはします。

## 実装内容

tests直下にデフォルトで作成されているTestCase.phpを拡張する。

```php:TestCase.php
    public function setUp()
    {
        parent::setUp();
        // phpunit実行のタイミングで一度だけテスト用DBに対してマイグレーションを実行する
        if (! self::$migrated) {
            foreach (\DB::select('SHOW TABLES') as $table) {
                $column_name = 'Tables_in_'.\DB::connection('')->getDatabaseName();
                \DB::statement('DROP TABLE IF EXISTS `' . $table->$column_name. '`');
            }
            \Artisan::call('migrate');
            self::$migrated = true;
        }
    }

    public function tearDown()
    {
        foreach (\DB::select('SHOW TABLES') as $table) {
            $column_name = 'Tables_in_'.\DB::connection('')->getDatabaseName();
            \DB::statement('TRUNCATE TABLE `' . $table->$column_name . '`');
        }
        parent::tearDown();
    }
```


# 公式に載っている方法だと、各テストfunction毎にmigration:rollbackからのmigrationが実行され結構時間がかかってしまう

https://laravel.com/docs/5.4/database-testing#using-migrations

というのもテストデータベースのmigration実行はsetUp()で呼び出されているためである。

- phpunit実行時にmigrationを実行したい場合にインポートする必要のあるtrait  

```php:DatabaseMigrations.php
trait DatabaseMigrations
{
    /**
     * Define hooks to migrate the database before and after each test.
     *
     * @return void
     */
    public function runDatabaseMigrations()
    {
        $this->artisan('migrate');

        $this->app[Kernel::class]->setArtisan(null);

        $this->beforeApplicationDestroyed(function () {
            $this->artisan('migrate:rollback');
        });
    }
}
```

- 各テストクラスの規定クラス  

```php:TestCase.php
    /**
     * Setup the test environment.
     *
     * @return void
     */
    protected function setUp()
    {
        if (! $this->app) {
            $this->refreshApplication();
        }

        $this->setUpTraits();

        foreach ($this->afterApplicationCreatedCallbacks as $callback) {
            call_user_func($callback);
        }

        Facade::clearResolvedInstances();

        Model::setEventDispatcher($this->app['events']);

        $this->setUpHasRun = true;
    }

    /**
     * Boot the testing helper traits.
     *
     * @return void
     */
    protected function setUpTraits()
    {
        $uses = array_flip(class_uses_recursive(static::class));

        if (isset($uses[DatabaseMigrations::class])) {
            $this->runDatabaseMigrations();
        }

        if (isset($uses[DatabaseTransactions::class])) {
            $this->beginDatabaseTransaction();
        }

        if (isset($uses[WithoutMiddleware::class])) {
            $this->disableMiddlewareForAllTests();
        }

        if (isset($uses[WithoutEvents::class])) {
            $this->disableEventsForAllTests();
        }
    }
```

よって
1. テストfunctionA()の実行
2. setUp()の実行（migration）
3. tearDown()の実行
4. テストfunctionB()の実行
5. setUp()の実行（migration）
6. tearDown()の実行
7. 以下繰り返し
といった形でmigrationが複数回実行されてしまう。アプリケーションに必要なテーブルを作成するだけであればmigration自体は1度だけ実行して、以降はtruncateでテーブルをきれいにするだけで十分であると考える。