laravelでDBテストコードを書く前の設定すべきこと

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

## 理由
テストDBを用意して、テストを実行する際、
`DatabaseMigrations`や`DatabaseTransactions`を使ってもDBに全く影響を与えないわけでは無いです。
> AUTO_INCREMENTの値など、一部は影響を受けてしまいます
[Laravel 5.4 で手軽にテストを書こう！ | 株式会社インフィニットループ技術ブログ](https://www.infiniteloop.co.jp/blog/2017/05/laravel-5-testing/)

### 実際に試してみました。
1. `use DatabaseTransactions;`が無い状態で、AUTO_INCREMENTのカラムがあるレコードを追加するコードのあるテストを実行(※以降テスト)
2. `use DatabaseTransactions;`がある状態テストを実行
3. `use DatabaseTransactions;`が無い状態テストを実行

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

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

[データベースのテスト 5.4 Laravel](https://readouble.com/laravel/5.4/ja/database-testing.html)

## テスト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](https://qiita.com/okashoi/items/92bc1b244e8efca8e67a)

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

### phpunit.xmlでDBを切り替えるパターン
phpunit.xmlファイル内に`<env name="DB_DATABASE" value="hoge_testing"/>`を追加。
追加する箇所は以下の通りです。

```xml
<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>
```

[[Laravel 5.2] Laravelでtest用DBにmigrateする方法 - Qiita](https://qiita.com/colorrabbit/items/f660a4b6a2575b75a0e7)

#### コマンドからmigrateを実行する場合
config/database.phpのmysqlの設定コピーして、databaseの箇所だけテストDBのDB名に書き換える。

```php
'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
```
[[Laravel 5.2] Laravelでtest用DBにmigrateする方法 - Qiita](https://qiita.com/colorrabbit/items/f660a4b6a2575b75a0e7)

### config をクリアする。

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

### DBの初期状態でデータベースのテストをする場合
テストクラスに以下メソッドを追加。

```php
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 
```

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

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

[Laravel5でテスト時にtesting設定を使う方法 - アルファブレンド プログラミングチップス](http://alphabrend.hatenablog.com/entry/2016/05/23/175249)

laravelのバージョンが5.5であれば`RefreshDatabase;`が使える。
`use RefreshDatabase;`を追加することで
テスト前にMigrationとSeederが実行される。
[LaravelでHTTPテスト - Qiita](https://qiita.com/Frog_woman/items/6a143af0a042dc853e88)


### テストを実行
'phpunit'をコマンドで実行


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

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

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

```php
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;
        }
    }
```

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