「PHPフレームワークLaravel入門 第2版」のユニットテストをLaravel 8で行う

##はじめに
Lravel 8になり、「PHPフレームワークLaravel入門 第2版」（いわゆる青本）のユニットテストの解説部分のコードが大幅に変更されました。
そこで、本の内容に沿って、Laravel 8でのユニットテストの記述方法を紹介したいと思います。

##テスト用データベースの準備
touchコマンドやDB Browserを利用して、databaseフォルダ内にdatabase_test.sqliteを作成します。

#####リスト7-36
変更はありません。

プロジェクトフォルダ内のphpunit.xmlを開き、`<php>`タグ内に以下を追記します。

```xml:phpunit.xml
<env name="DB_DATABASE" value="database\database_test.sqlite"/>
```

##ダミーレコードの用意
###Userファクトリ
#####リスト7-37
デフォルトで用意されているdatabase\factories\UserFactory.phpの内容がクラスベースになっています。
UserFactory.phpは最初から用意されている物を変更しないでそのまま利用します。

###Personファクトリ
####ファクトリの作成

ダミーレコードの作成の処理をUserFactory.phpに記述するのではなく、
新たにPersonモデルに対応するファクトリを作成する必要があります。以下のコマンドを実行して下さい。

```
php artisan make:factory PersonFactory
```
この時、Personモデルであれば、PersonFactoryといった形でモデル名と対応するようにファクトリの名前を指定して下さい。
後ほど登場するfactoryメソッドは名前が一致していて、接尾辞がFactoryであるファクトリを自動的に探し出します。

#####リスト7-38
作成したPersonファクトリの編集に移ります。
database\factories\PersonFactory.phpを開き、definition()メソッドの戻り値を以下のように変更します。

```php:database\factories\PersonFactory.php
<?php

namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;

class PersonFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'mail' => $this->faker->safeEmail,
            'age' => random_int(1,99),
        ];
    }
}
```

##ユニットテストのスクリプト作成
#####リスト7-39
変更はありません。

以下のコマンドを実行すると、tests\Feature\HelloTest.phpが作成されます。

```
php artisan make:test HelloTest
```


##一般的な値のテスト
#####リスト7-40
変更はありません。

####phpunitの実行

phpunitの実行は以下のコマンドを入力します。

```
verdor\bin\phpunit
```
もしくは、以下のコマンドでも同じです。

```
php artisan test
```

##指定アドレスにアクセスする
#####リスト7-41
変更はありません

#####リスト7-42
tests\Feature\HelloTest.phpを以下のように編集します。

```php:tests\Feature\HelloTest.php
<?php

namespace Tests\Feature;

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

class HelloTest extends TestCase
{
    use RefreshDatabase; //各テスト後のデータベースリセット

    public function testHello()
    {
        $this->assertTrue(true);

        $response = $this->get('/');
        $response->assertStatus(200);

        $response = $this->get('/hello');
        $response->assertStatus(302);

        /** @var \Illuminate\Contracts\Auth\Authenticatable $user */
        $user = User::factory()->create();
        $response = $this->actingAs($user)->get('/hello');
        $response->assertStatus(200);

        $response = $this->get('/no_route');
        $response->assertStatus(404);
    }
}
```
#####モデルのインスタンス化方法の変更
`factory(User::class)->create();`から
`$user = User::factory()->create();`のように静的メソッドを使用するように変更されています。

#####マイグレーション・ロールバック方法の変更
利用するトレイトが
`use DatabaseMigrations;`から
`use RefreshDatabase;`に変更されています。
これはテスト前に未実行のマイグレーションを実行し、テスト終了後にはデータベースを初期状態へ戻すためのものです。

#####エラー対策

`/** @var \Illuminate\Contracts\Auth\Authenticatable $user */`で変数の型を明記しています。
actingAsの実引数部分でエラーが出るので入れています。

##データベースをテストする
#####リスト7-43
tests\Feature\HelloTest.phpを以下のように編集します。

```php:tests\Feature\HelloTest.php
<?php

namespace Tests\Feature;

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

class HelloTest extends TestCase
{
    use RefreshDatabase; //各テスト後のデータベースリセット

    public function testHello()
    {
        // ダミーで利用するデータ
        User::factory()->create([
            'name' => 'AAA',
            'email' => 'BBB@CCC.COM',
            'password' => 'ABCABC',
        ]);
        User::factory(10)->create();

        $this->assertDatabaseHas('users', [
            'name' => 'AAA',
            'email' => 'BBB@CCC.COM',
            'password' => 'ABCABC',
        ]);

        // ダミーで利用するデータ
        Person::factory()->create([
            'name' => 'XXX',
            'mail' => 'YYY@ZZZ.COM',
            'age' => 123,
        ]);
        Person::factory(10)->create();

        $this->assertDatabaseHas('people', [
            'name' => 'XXX',
            'mail' => 'YYY@ZZZ.COM',
            'age' => 123,
        ]);
    }
}
```
本では上記のリスト7-43で最後となっていますが、このままだとエラーが発生するかと思います。

####HasFactoryトレイトの利用

factoryメソッドを利用する為には、モデル側でHasFactoryトレイトを利用する必要があります。
上記のリスト7-43を実行した際にPersonモデルにトレイトが記述されていませんので、エラーが発生しました。
以下のように`use HasFactory;`を追記するとfactoryメソッドが使えるようになります。

```php:app\Models\Person.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Person extends Model
{
    use HasFactory; //追加する
    protected $guarded = array('id');

    public static $rules = array(
        'name' => 'required',
        'mail' => 'email',
        'age' => 'integer|min:0|max:150'
    );

    public function getData()
    {
        return $this->id . ': ' . $this->name . ' (' . $this->age . ')';
    }

}
```
これでテストが通るようになったかと思います。
以上で完了になります。お疲れさまでした！