[Laravel]  テスト実行時にテスト用データベースへ切り替わる仕組み


## 要点
Laravelでは、**テスト用DBの作成と切り替えを全て自動で行ってくれます**。
一連の仕組みに関わっているファイルは以下の通り。

- テスト用DBの作成　→ `docker-compose.yml`および`docker/mysql/create-testing-database.sh`
- テスト用DBヘの切り替え → `phpunit.xml`および`.env`


## 前提
**Laravel Sailを使ったDocker環境**で開発をしているという前提で話を進めます。

- macOS    13.5.2
- PHP    8.2.11
- Laravel Framework    10.28.0
- laravel/sail    v1.25.0
- Docker    23.0.5
- laravel/breeze    v1.25.0


## 本題
### はじめに
アプリケーション開発において、テストを実行すると**DBに保存されているデータが全て消去されます**。
そのため、テストを行う際は必ずテスト用DBに切り替える必要があります。

:::note info
<details>
<summary>Q. そもそも、なぜテスト実行時にDBの全データが消去されるの？</summary>

A. １つ１つのテストの独立性を保つため。

解説:
テスト（機能テスト）では、DBへのデータ登録・取得・更新等のDB接続も含めた検証を行うため、各テストの実行後にDBの全データを消去し初期化を行います。もし仮に初期化を行わなかった場合、後続の別のテストに影響を与えてしまい、正しいテスト結果が得られなくなってしまう可能性があります。

ちなみに、DBの初期化を行うためには、テストクラス内で`Illuminate\Foundation\Testing\RefreshDatabase`トレイトを使用すればOKです。
~~~tests/Features/ExampleTest.php
<?php

namespace Tests\Feature;

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

class ExampleTest extends TestCase
{
    // RefreshDatabaseトレイトを使用し、テスト実行後にDBを初期化
    use RefreshDatabase;

    /**
     * 基本的な機能テスト例
     */
    public function test_basic_example(): void
    {
        $response = $this->get('/');

        // ...
    }
}
~~~
詳細はLaravelドキュメントをご確認下さい。
→ https://readouble.com/laravel/10.x/ja/database-testing.html

</details>
:::


「テスト用DBに切り替える」ためには、
切り替えのための設定が必要なのはもちろんですが、まずその前にテスト用DB自体を作成する必要があるいうことになります。

「手順が多くて大変じゃん...」と思いきや、
Laravelでは**テスト用DBの作成と切り替えを全て自動で行ってくれる**ため、自分で何も設定する必要はありません。

それでは、テスト用DBの作成と切り替えが実際にどのような仕組みによって実現されているのか見ていきましょう。


### テスト用DBの作成
テスト用DBの作成には、`docker-compose.yml`と`docker/mysql/create-testing-database.sh`というファイルが関係しています。

まず、`docker-compose.yml`の`services.mysql.volumes`の箇所で、`docker/mysql/create-testing-database.sh`を実行しています。
~~~docker-compose.yml
#（略）

volumes:
    - 'sail-mysql:/var/lib/mysql'
    - './docker/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
~~~

そして、`docker/mysql/create-testing-database.sh`では、`testing`という名前のDBを作成しています。
~~~docker/mysql/create-testing-database.sh
#!/usr/bin/env bash

mysql --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL
    CREATE DATABASE IF NOT EXISTS testing;
    GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%';
EOSQL
~~~

これによって、私たちが自分で`testing` DBを作らなくて済んでいるわけですね。

### テスト用DBへの切り替え

では、テスト用DBへの切り替えはどのように行っているのでしょうか。
これには、`phpunit.xml`と`.env`というファイルが関係しています。

`phpunit.xml`では、テスト時に利用する環境変数の定義をすることができます。

~~~phpunit.xml
<!-- 略 -->
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <!-- ↓ テスト用DB 'testing'を使用する設定 -->
        <env name="DB_DATABASE" value="testing"/>　

        <!-- 略 -->
    </php>
~~~

`<env name="DB_DATABASE" value="testing"/>`という記述により、テスト時のみ`DB_DATABASE`環境変数の値が'testing'へと切り替わり、
それによって`testing`DBへ接続するようになるという仕組みです。

ちなみに、環境変数を定義している`.env`ファイルの`DB_DATABASE`では、開発時に使用するDBが定義されています。
~~~.env
DB_DATABASE=developing  #（開発時に使用するDB名）
~~~
上の場合、開発時には`developing`というDBを使用するということになります。
そしてテスト時のみ、`phpunit.xml`に定義された`testing`DBへ切り替えるということになりますね。

---

以上が、Laravelにおける「テスト実行時にテスト用データベースへ切り替わる仕組み」でした。

このあたりの知識は、ソフトウェアテストについて学びたての頃は学習するのに苦労するポイントかなと思います。
この記事がお役に立てたなら幸いです。


<!-- アプリケーションのテスト（単体テストや機能テストなど）は、**アプリの品質を保つ上で非常に重要**です。

仮にテストコードを書かずテストを行わなかった場合、
　「アプリ内に予期せぬ重大なバグが含まれていないか」
　「コードの修正や追加によって新たなバグが発生していないか」
といったことを検証できません。

よって、コードによるテストが可能な箇所については極力テストコードを書き、テストを行う必要があります。
しかし、初学者の方々にとって、テストコードを書く以前に**テスト環境をきちんと設定すること**自体が難しいのではないでしょうか。
私自身、テストを実行したらデータベース内のデータが全て消えた経験があります。-->