LoginSignup
11
16

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-05-22

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

DatabaseMigrations.phpは使用すると処理時間が長くなってしまうので自前で処理を組む。
詳細な理由はこちら

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

実装内容

tests直下にデフォルトで作成されているTestCase.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が実行され結構時間がかかってしまう

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

  • phpunit実行時にmigrationを実行したい場合にインポートする必要のあるtrait
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');
        });
    }
}
  • 各テストクラスの規定クラス
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でテーブルをきれいにするだけで十分であると考える。

11
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
16