laravelでテストコードを書く前に設定しておいた方が良いこと

データベースのテストをする際には、テスト用のDBを用意して、そのDBでテストした方が良い。

理由

テストDBを用意して、テストを実行する際、
DatabaseMigrationsDatabaseTransactionsを使ってもDBに全く影響を与えないわけでは無いです。

AUTO_INCREMENTの値など、一部は影響を受けてしまいます
Laravel 5.4 で手軽にテストを書こう! | 株式会社インフィニットループ技術ブログ

実際に試してみました。

  1. use DatabaseTransactions;が無い状態で、AUTO_INCREMENTのカラムがあるレコードを追加するコードのあるテストを実行(※以降テスト)
  2. use DatabaseTransactions;がある状態テストを実行
  3. use DatabaseTransactions;が無い状態テストを実行

そうするとAUTO_INCREMENTのカラムが連番にリませんでした。

テストをすることで少しでも本番DBなどに影響を与えてしまうのは良くないので、テストする時用でDBを切り替えてた方が良いと思いました。

データベースのテスト 5.4 Laravel

テストDBでのテスト方法

テスト用のDBを作成する。

hoge_testing
※surfixに _testingをつけるとテストDBだということが分かりやすい。

envファイルでDBを切り替えるパターン

開発環境、本番環境、ローカル環境などでテストDB名が違う場合は、このパターンを使うのが良いと思う。

テスト用のenvファイルを作る

ファイル名
.env.testing

DB名の箇所だけ書き換える

DB_DATABASE=hoge_testing

テストDBに対してmigrateを実行する方法

php artisan migrate --seed --env=testing

--env=testingをつけることで.env.testingのenvファイルを読んでmigrateを実行する事ができる。
Laravel5 で別環境(APP_ENV)で artisan コマンドを実行 - Qiita

テストコード内でmigrate:refreshなどを実行しているのであれば、コマンドでのmigrateの実行は不要かと思います。

phpunit.xmlでDBを切り替えるパターン

phpunit.xmlファイル内に<env name="DB_DATABASE" value="hoge_testing"/>を追加。
追加する箇所は以下の通りです。

<php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="DB_DATABASE" value="hoge_testing"/>
</php>

https://qiita.com/colorrabbit/items/f660a4b6a2575b75a0e7

コマンドからmigrateを実行する場合

config/database.phpのmysqlの設定コピーして、databaseの箇所だけテストDBのDB名に書き換える。

'mysql_testing' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => 'hoge_testing',
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'modes' => [

            ],
            'engine' => null,
        ],

後は以下のコマンドを実行すればテストDBに対してmigrate:refreshを実行することが出来る。

php artisan migrate:refresh --database=mysql_testing

https://qiita.com/colorrabbit/items/f660a4b6a2575b75a0e7

config をクリアする。

php artisan config:clear

このコマンドを実行せずにArtisan::call('migrate:refresh');など実行があるテストを実行したりすると、本番DBのデータが飛んだりするので要注意です。

DBの初期状態でデータベースのテストをする場合

テストクラスに以下メソッドを追加。

public function setUp() {
    parent::setUp();
    Artisan::call('migrate:refresh');
    Artisan::call('db:seed');
}

migrate:refreshを実行する際はmigrationファイルのdownメソッドにupメソッドが行った操作を元戻す処理 を書いておく必要があります。
https://readouble.com/laravel/5.3/ja/migrations.html

外部キー制約がある場合

以下のエラーが出る時があります。

SQLSTATE[23000]: Integrity constraint violation: 1217 Cannot delete or update a parent row: a foreign key constraint fails 

以下の様にして一時的に外部キー制約を解除する。

public function down()
{
    Schema::disableForeignKeyConstraints();
    Schema::dropIfExists('user');
    Schema::enableForeignKeyConstraints();
}

Laravel5でテスト時にtesting設定を使う方法 - アルファブレンド プログラミングチップス

laravelのバージョンが5.5であればRefreshDatabase;が使える。
use RefreshDatabase;を追加することで
テスト前にMigrationとSeederが実行される。
LaravelでHTTPテスト - Qiita

テストを実行

'phpunit'をコマンドで実行

おまけ

setUpBeforeClass以外の方法でクラスで一回だけ実行するメソッドを作る方法

migrate:refreshdb:seedを、setUpメソッドに記述すると、テストにかかる時間が結構長くなってしまうので、クラスで一回だけ実行すれば場合はsetUpBeforeClassというメソッド内で実行すると良いようです。しかしsetUpBeforeClass内でenv()logger()などのlaravelで用意してくれているhelperは使えませんでした。
以下の様な処理がしたかったがenvが呼べない。

    public static function setUpBeforeClass()
    {
        if(env('DB_DATABASE_TESTING') == 'hoge_testing'){
            Artisan::call('migrate:refresh');
            Artisan::call('db:seed');
            self::insertTestData();
        }
    }

なのでsetUpメソッドを一回のみ実行するようにしました。

private static $isSetup = false;

    public function setUp()
    {
        parent::setUp();
        if(env('DB_DATABASE_TESTING') == 'hoge_testing' && self::$isSetup === false){
            Artisan::call('migrate:refresh');
            Artisan::call('db:seed');
            self::$isSetup = true;
        }
    }

もっといい方法あるよって方は教えてください。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.