[laravel] phpunitでfactoryを使ってpostを送る方法

##はじめに
phpunitを使ってユニットテストを書こうとしたところつまづいてしまったので備忘録としてまとめてみました。
誤りがございましたら、コメントいただけるとありがたいです。
laravel6.20.37
phpunit8.0
##機能の把握
質問サイトでの投稿機能です。

```php:QuestionController
<?php

namespace App\Http\Controllers;

use App\Http\Requests\QuestionRequest as QuestionRequest;
use App\Question;

class QuestionController extends Controller
{
  public function store(QuestionRequest $request, Question $question)
    {
        $question->fill($request->all());
        $question->user_id = $request->user()->id;
        $question->save();
        return redirect(route('home'));
    }
 
}

```

##テスト

```php:QuestionControllerTest.php
<?php

namespace Tests\Feature;

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

class QuestionControllerTest extends TestCase
{
    use RefreshDatabase;

    public function testQuestionStore()
    {
        $user = factory(User::class)->create();
        $question = factory(Question::class)->create();
        $response = $this->withoutMiddleware()->actingAs($user)
            ->post(route('questions.store'), ['question' => $question]);
        $response->assertRedirect(route('home'));
        $this->assertDatabaseHas('questions', [
            'id' => $question->id,
        ]);
    }
}

```

###RefleshDatabase
 
```php
use RefreshDatabase;
```
RefreshDatabaseトレイトを使用し、データベースをリセットしています。
また、テスト中のトランザクション処理をテスト終了後になかったことにしています。

###factory関数

```php
$question = factory(User::class)->create();
$question = factory(Question::class)->create();
```
factory関数を使用することでテストに必要なモデルのインスタンスを生成できます。
ここでは、UserモデルとQuestionモデルをDBに保存しています。
また、factory関数を利用するにはモデルのファクトリを用意する必要があります。

```php:UserFactory.php
use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
    ];
});
```
```php:QuestionFactory.php
use App\Question;
use Faker\Generator as Faker;
use App\User;

$factory->define(Question::class, function (Faker $faker) {
    return [
        'title' => $faker->text(50),
        'body' => $faker->text(255),
        'solution' => $faker->boolean(),
        'user_id' => function () {
            return factory(User::class);
        },
    ];
});
```
###post送信

```php
$response = $this->withoutMiddleware()->actingAs($user)
    ->post(route('questions.store'), ['question' => $question]);

```
postを送信するテストでは、ミドルウェアを無効にする必要がある場合があるため、withoutMiddleware()を使い、一時的にミドルウェアを無効にしています。
actingAs($user)では生成したUserモデルでログインした状態を作り出しています。
postの第一引数ではuri、第二引数で'question'をkeyにしたQuestionモデルを渡しています。

https://stackoverflow.com/questions/46325790/phpunit-expected-status-code-200-but-received-419-with-laravel

###assertRedirect
```php
$response->assertRedirect(route('home'));
```
次にassertRedirectメソッドでpost送信された後にリダイレクトされているかをチェックしています。

###DBテスト
```php
$this->assertDatabaseHas('questions', [
    'id' => $question->id,
]);
```

最後にassertDatabaseHasメソッドでデータベーステーブルに対して条件に合ったレコードが存在するのかをテストしています。

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

questionsテーブルに対し、先程作成したQuestionモデルのidが存在するかをテストしています。
Questionモデルはこのような形で$questionに格納されています。

```
{"id":1,"title":"Omnis id vel explicabo laboriosam voluptatum.","body":"Est ut saepe quam asperiores placeat est accusantium. Est sapiente sapiente tenetur eum soluta est aut. Dolores dolores quia autem quia ad id. Qui molestias quibusdam est corrupti qui necessitatibus.","solution":true,"user_id":3,"updated_at":"2021-11-23 13:01:14","created_at":"2021-11-23 13:01:14"}
```
そのため、$question->idとすることでpostによってDBに保存されたレコードとファクトリーで作成されたモデルのレコードが同じであることをテストしています。

##最後に
factryを利用してpost送信するテストがあまりなかったため、記事にしてみました。
テストは大事ですのでどんどん書いていきたいと思います。