LaravelのUnitTestを使ってみる

# 下準備
###`phpunit.xml`を編集する
`DB_DATABASE`にテスト用のDB名を記載します。

```phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="DB_CONNECTION" value="mysql"/>
        // テスト用のDB名を記載する
        <server name="DB_DATABASE" value="laravel_test"/>
        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
    </php>
</phpunit>
```

### .env.testingを作成・編集する
`.env`ファイルをコピーして`.env.testing`にリネームし、`DB_DATABASE`にテスト用のDB名を記載します。

```.env.testing
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_test
DB_USERNAME=root
DB_PASSWORD=
```

### テスト用テーブルを作成
マイグレーションファイルを作成します。

```
php artisan make:migration create_articles_table
```

構成はシンプルにします。

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string('title', 100);
            $table->text('content');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}
```

テスト用のDBにテーブルを作るには、`php artisan migrate`コマンドにオプションで`--env=testing`を付けて実行します。

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

これにより、`.env.testing`に記載されているDBに対してマイグレーションを実行するようになります。

```
+------------------------+
| Tables_in_laravel_test |
+------------------------+
| articles               |
| failed_jobs            |
| migrations             |
| users                  |
+------------------------+
```

```
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| title      | varchar(100)        | NO   |     | NULL    |                |
| content    | text                | NO   |     | NULL    |                |
| created_at | timestamp           | YES  |     | NULL    |                |
| updated_at | timestamp           | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
```

テスト用DBのlaravel_testにarticlesテーブルが作成されました。

# テストクラス作成
以下のコマンドで`tests/Unit`ディレクトリ以下にテスト用のクラスを作成することができます。

```
php artisan make:test [テストクラス名] --unit
```

`Unit`ディレクトリ以下に作成するクラスには、単体テストレベルの内容を記述していきます。

```ArticleTest.php
<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ArticleTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

```


`--unit`オプションを外すと`tests/Feature`ディレクトリ以下にテスト用のクラスを作成できます。
`Feature`ディレクトリ以下に作成するクラスには、機能テストレベルの内容を記述します。

# テストコード実装

モデルを作成し、記事データを保存するテストコードを実装してみます。

```
php artisan make:model Article
```

モデル作成後、`ArticleTest.php`を編集します。

```ArticleTest.php
<?php

namespace Tests\Unit;

//use PHPUnit\Framework\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Article;

class ArticleTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }

    /**
     * (1) 記事テーブルにデータをインサート
     */
    public function testInsertTest()
    {
        $article = new Article;
        $article->title = '記事タイトル';
        $article->content = '記事本文';
        $result = $article->save();

        $this->assertTrue($result);
    }
}
```

`php artisan test`コマンドを実行すると、`testInsertArticleTest`関数が実行されたことが分かると思います。

```
  PASS  Tests\Unit\ArticleTest
  ✓ example
  ✓ insert article test


   PASS  Tests\Unit\ExampleTest
  ✓ basic test


   PASS  Tests\Feature\ExampleTest
  ✓ basic test

  Tests:  4 passed
  Time:   0.35s
```

テーブルを確認すると、`testInsertArticleTest`関数に記述したデータが保存されています。

```
+----+--------------+----------+---------------------+---------------------+
| id | title        | content  | created_at          | updated_at          |
+----+--------------+----------+---------------------+---------------------+
|  1 | 記事タイトル | 記事本文 | 2020-07-27 06:27:12 | 2020-07-27 06:27:12 |
+----+--------------+----------+---------------------+---------------------+
```

ちなみに、`use RefreshDatabase;`をテストクラスのスコープ内に記述すると、保存したデータの削除までを行ってくれるため、テストデータを手動で削除する手間を省くことができます。

# ランダムなダミーデータの作成
### FactoryとFaker
ファクトリーを作成します。

```
php artisan make:factory [ファクトリ名] --model=[対象のモデル名]
```

`database/factory`ディレクトリ以下にファクトリーファイルが作成されます。
以下の例では`title`カラムに3単語から構成される文、`content`カラムに文章を保存するように設定しています。

```ArticleFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Article;
use Faker\Generator as Faker;

$factory->define(Article::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence(3),
        'content' => $faker->text,
    ];
});

```

`tests/Unit/ArticleTest.php`に以下のメソッドを追記します。
`ArticleFactory.php`で設定したテストデータを3個追加するメソッドです。

```ArticleTest.php
    /**
     * (2) 記事テーブルにランダムテストデータをインサート
     */
    public function testInsertFactoryTest()
    {
        $articles = factory(Article::class, 3)->create();
        $count = count($articles);

        $this->assertEquals(3, $count);
    }
```

`php artisan test`を実行してarticlesテーブルを確認すると、`testInsertFactoryTest`関数のテストデータが3つ保存されました。

```
*************************** 1. row ***************************
        id: 1
     title: 記事タイトル
   content: 記事本文
created_at: 2020-07-27 07:56:16
updated_at: 2020-07-27 07:56:16
*************************** 2. row ***************************
        id: 2
     title: Quasi a.
   content: Consequuntur veritatis aut hic id rem minima. Occaecati et quia hic earum quasi eos. Omnis repellat commodi vitae error ut et provident ipsam.
created_at: 2020-07-27 07:56:17
updated_at: 2020-07-27 07:56:17
*************************** 3. row ***************************
        id: 3
     title: Mollitia aut nostrum sint.
   content: Esse saepe distinctio provident. Placeat unde voluptates expedita et consequatur quas. Excepturi ut quo est et provident velit consequatur.
created_at: 2020-07-27 07:56:17
updated_at: 2020-07-27 07:56:17
*************************** 4. row ***************************
        id: 4
     title: Omnis mollitia omnis illo.
   content: Nam odit aut quam est sunt. Ut ullam perferendis error placeat et. Sed non tenetur esse.
created_at: 2020-07-27 07:56:17
updated_at: 2020-07-27 07:56:17
```

手作業で１つずつデータを登録したりする時間を短縮できるので便利ですね。