【Laravel】PHPUnitでの単体テストについて簡単にまとめて、実装していく

#目次
[1.単体テストとは？](#1-単体テストとは？)
[2.単体テストを書くメリット](#2-単体テストを書くメリット)
[3.単体テストの作成](#3-単体テストの作成)
[4.参考](#4-参考)

#1. 単体テストとは？

**関数や、メソッド単位で動作確認を行う。**

#2. 単体テストを書くメリット/デメリット
## メリット
* モジュールが結合される前にテストができるため、問題の原因特定がしやすい
* コードの作成と同時にテストコードが書いていくため、テストケースが資産として残っている。
つまり、後々の改修がしやすい
* PHPUnitを使うことで自動化できる→コマンド一つで動作確認が可能（改修時の確認においても）

## デメリット
* 開発者の開発負担が高くなる

#3. 単体テストの作成

##FeatureとUnit

どちらもtestsディレクトリはいかに存在するディレクトリです。
どちらのディレクトリでもテストを記載できるのですが、どのように使い分けるべきかまとめました。

| ディレクトリ | 使い分けについての説明 |
| ---- | ---- |
| Feature | １つのHTTPリクエストの動作テスト / UnitよりもTest範囲が広い |
| Unit | 作成したクラスそれぞれに対応するテストクラス作成（Service、Model、Middlewareなど） / 1つ1つのメソッドなどの動作検証 / （ControllerはFeatureに。）|

以下参考記事です。

https://qiita.com/minato-naka/items/cd1d0408e412fe86edc5

##実装していく

と、まとめてみたものの実際に実装しないとよく分からないので、

以前、実装したコードを元にどんどんテストしていきます。

| - 意識する事 - |
| ---- |
| Feature / Unitを使い分けて書く |
| メソッド単位〜HTTPリクエストまで幅広くテストコードをどんどん書いていく |

```
// Featureディレクトリにテストを生成する
$ php artisan make:test Post/ToPostTest

// Unitディレクトリにテストを生成する
$ php artisan make:test Post/ToPostTest --unit
```

テストを書いていきます。

### ●＜HTTPリクエストテスト＞

基本的な機能テスト

```test.php
<?php

namespace Tests\Feature\Post;

use Tests\TestCase;

class ToPostTest extends TestCase
{
    public function testGetPage()
    {
        $response = $this->get('/post/topost');
        $response->assertStatus(302);
    }
}
```

### ●＜データ検索：存在チェック＞

存在するもの、しないもの、どちらも検索をかけてチェックしています。

```test.php
<?php

namespace Tests\Feature\Post;

use Tests\TestCase;

class ToPostTest extends TestCase
{
    /**
     * 投稿用のお風呂の検索
     *
     * @return void
     */
    public function testSearchBathName()
    {
        // 都道府県検索
        $place = Bath::where('place', '東京都')->first();
        $this->assertNotNull($place); // Nullで無ければtrue
        $place = Bath::where('place', '韓国')->first();
        $this->assertNull($place);
        $place = Bath::where('place', 'トウキョウ')->first();
        $this->assertNull($place);
        $place = Bath::where('place', 'tokyo')->first();
        $this->assertNull($place);
        $place = Bath::where('place', '東京')->first();
        $this->assertNull($place);

        // キーワード検索：あいまい検索
        $keyword1 = Bath::where('name', 'like', "%ああ%")->first(); // nameカラムに、「あ」があることの確認
        $this->assertNotNull($keyword1);
        $keyword2 = Bath::where('name', 'like', "%a%")->first(); // nameカラムに、「a」があることの確認
        $this->assertNotNull($keyword2);
        $keyword3 = Bath::where('name', 'like', "%かかかかかかk%")->first(); // nameカラムに、「かかかかかかk」がないことの確認
        $this->assertNull($keyword3);
    }
}
```

何かもっといいやり方がありそう。。。

###●＜テストデータ投稿テスト＞

ファクトリーを作成してテストを行います

```
$ php artisan make:factory PostFactory
```

```PostFactory.php
<?php

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

use App\Models\Post;
use Faker\Generator as Faker;
use Illuminate\Http\UploadedFile;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'title' => $faker->name,
        'thoughts' => $faker->name,
        'main_image_path' => UploadedFile::fake()->create('main.jpeg', 10)->store('uploads'),
        'eval_cd' => $faker->randomElement([0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]),
    ];
});

```

```ToPostTest.php
<?php

namespace Tests\Feature\Post;

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

/**
 * お風呂投稿 Featureテスト
 */
class ToPostTest extends TestCase
{

    use DatabaseTransactions; // FactoryのテストデータをDBに保存させない

    /**
     * 投稿画面 投稿処理、投稿後リダイレクト、DB保存テスト
     *
     * @return void
     */
    public function testPost()
    {
            $user = factory(User::class)->create();
            $post = factory(Post::class)->create();

            $this->actingAs($user)->get('/post/mypost');

            $this->withoutExceptionHandling(); // Exception発生後のスルー回避

            $data = [
                'title' => $post->title,
                'thoughts' => $post->thoughts,
            ];

            $response = $this->withoutMiddleware()->post('/post/topost', $data); // ミドルウェアを通さない

            $response->assertRedirect('/post/mypost'); // リダイレクトテスト
            $this->assertDatabaseHas('posts', $data); // DBテスト（postsテーブル）
    }
}
```

#### ```factory()->create()```
・Eloquentのsaveメソッドを使用しデータベースへ保存
・```factory()->make()```だと、インスタンスだけ作成

#### ```use DatabaseTransactions;```
・FactoryのテストデータをDBに保存させないようにしています。

#### ```$this->withoutExceptionHandling()```の存在意義について

・記載がないと、テスト対象のスクリプト内でExceptionが発生してもスルーされる
・Webサーバーはステータス500を返しているはずのところを、```$response->assertStatus(200)```が通る
・例外処理のテストでもない限り、```$this->withoutExceptionHandling()```は記載すべき

https://qiita.com/Fea/items/7d70caa52e73c3fd8dfe

https://mutimutisan.com/laravel-test2

#4. 参考 

https://readouble.com/laravel/6.x/ja/testing.html

https://re-engines.com/2020/04/13/laravel-test/