Laravel+vueでなんか作る(テスト編①)～テスト用環境を作る～

UdemyのLaravel on Docker + Vueコースを履修した際に作ったバックエンドの環境に、テストを追加できないか試した際、多少のガッチャがあったので備忘録をまとめてみました。

- コースはこちら：[Vue 3 and Laravel: A Practical Guide with Docker](https://www.udemy.com/course/vue-laravel-admin/)
- 環境構築の備忘録はこちら：[Laravel+vueでなんか作る(Laravel基礎編①)](https://qiita.com/SkipEveryLunch/items/e29a8da64091233881af)

##まずはテスト用のデータベースを追加する
テストの際には毎回トランザクションがリセットされないと、データベースのレコードがハチャメチャなことになってしまいます。
実際``DatabaseTransactions``という機能を使えば毎回トランザクションがリセットされるようなのですが、完璧にデータベースへの影響を抑え込める！ というわけではなく、意図せぬ副作用のようなものが時々起きてしまうそうです。
というわけで、一応テスト用のデータベースを作っておきます。

```yml:docker-compose.yml
version: '3.8'
services:
  backend:
    container_name: backend
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8000:8000
    volumes:
      - .:/app
    depends_on:
      - db

  db:
    container_name: db
    image: mysql:5.7.22
    environment:
      MYSQL_DATABASE: admin
      MYSQL_USER: root
      MYSQL_PASSWORD: root
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./storage/dbdata:/var/lib/mysql

  db-test:
    container_name: db-test
    image: mysql:5.7.22
    environment:
      MYSQL_DATABASE: admin
      MYSQL_USER: root
      MYSQL_PASSWORD: root
      MYSQL_ROOT_PASSWORD: root
```

##.envで環境設定をする
続いて、.envを複製して.env.testingを作成します。
Laravelはテストの際は、.envではなく.env.testingに書かれた環境を元にテストを実行するとのことです。とても便利ですね。
という訳でテスト用dbを参照してくれるよう、.env.testingの内容のうち、書き換える必要のある部分を書き換えます。

```:.env
APP_ENV=testing
APP_KEY=

(中略）

DB_CONNECTION=mysql
DB_HOST=db-test
DB_PORT=3306
DB_DATABASE=admin
DB_USERNAME=root
DB_PASSWORD=root
```

仕上げに、コンソールに以下のコマンドを打ってAPP_KEYを生成します。

```
php artisan key:generate --env=testing
```

以上でテストの際のみ、テスト用のデータベースを参照してくれるようになりました。
なお、このあたりの流れは以下の記事を参考にさせて頂きました。

- [Laravel 8.x Docker環境でテスト用コンテナを立ち上げてテストを実行する](https://qiita.com/tokiya_takai/items/99583772693051ab2465)

##マイグレーション
``sudo docker exec -it backend sh``でバックエンドに入り、マイグレーションを実行します。今回はシーディングはせず、データベースにはカラムは入っているけれどもレコードは空っぽのままという感じで行きます。

```
php artisan migrate --env=testing
```

##テストを書く

続いて簡単なテストを一つ作成してみます。

```
php artisan make:test UserTest
```
これで``tests/Feature``下にUserTest.phpが生成されます。

```php:tests/Feature/UserTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\User;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function test_index()
    {
        $expectedNum = random_int(1,9);
        User::factory($expectedNum)->create();
        $response = $this->get('/api/');
        $response->assertJsonCount($expectedNum);
    }
}
```
なんだかイケてないテストな気がしますが、phpunitの作法が全く分からないので、おいおい勉強するという形で。今はこんなもんでいいでしょう……。

また、TestCase.phpを修正し、トランザクションをテストごとにリセットされる仕様に変更します。

```php:tests/TestCase.php
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
//ここを追加

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;
    use DatabaseTransactions;
//ここも追加
}
```

あとはTestCaseを継承するそれぞれのテストの中でも、``DatabaseTransactions``が機能してくれます。

##テストを実行する
テストを実行する際にも、``sudo docker exec -it backend sh``でバックエンドに入る必要があります。テスト内でシーディングしているから当然ですね。

```
php artisan test
```
これでテストできます。