LaravelのAPIのテストにおいて、APIのリクエスト手法を間違えてテストに失敗した話

Laravelの更新系APIのテストにおいて、APIのリクエスト手法を間違えたばかりにテストが失敗して悩んだ話を書きます。
ちゃんと公式ドキュメントを読めば当たり前のことなのですが、APIのテストには`json`メソッドを使いましょう。

APIに関連付けられたフォームリクエストが実行されるとき、
例えば`form`タグを`submit`して、バリデーションエラーが発生したら入力画面にリダイレクトしてほしい。
また、AJAXのAPIとしてリクエストし、バリデーションエラーならばJSONでエラーメッセージを返してほしい。
HTTPステータスコードでいうと、リダイレクトは302、バリデーションエラーのJSONレスポンスは422となります。
Laravelでバリデーションするときに使われるフォームリクエストは、この辺りの処理を自動でやってくれます。ありがたい:pray:
公式ドキュメントにもちゃんと書かれています。

> If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.

https://laravel.com/docs/6.x/validation#form-request-validation

そのHTTPステータスコードの切り替えに関わるAPIの呼び出し方をテストで再現しなかったばかりにテストが失敗して悩んでしまいました。
再現するために、ちゃちゃっとLaravelで更新系のAPIを作ってみます。使うLaravelのバージョンは6.xです。

```sh
$ composer create-project --prefer-dist laravel/laravel apisample
$ cd apisample
$ php artisan make:request UpdateUserNameRequest
$ php artisan make:controller UpdateUserNameApi
$ php artisan make:test UpdateUserNameApiTest
```


```routes/api.php 
<?php

use Illuminate\Http\Request;

Route::patch('/update_user_name', 'UpdateUserNameApi');
```

```app/Http/Requests/UpdateUserNameRequest.php 
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateUserNameRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required',
        ];
    }
}
```

名前は必須だよというフォームリクエストです。

```app/Http/Controllers/UpdateUserNameApi.php 
<?php

namespace App\Http\Controllers;

use App\Http\Requests\UpdateUserNameRequest;

class UpdateUserNameApi extends Controller
{
    public function __invoke(UpdateUserNameRequest $request)
    {
        // 更新処理

        return response()->json(['message' => '更新完了']);
    }
}
```

とりあえずの更新APIなので、どのユーザの名前を更新するんやい!! という点は置いといてください:pray:

次にテストです。まずは私が最初に書いて失敗したテストです。

```tests/Feature/UpdateUserNameApiTest.php 
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class UpdateUserNameApiTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testApi()
    {
        // エラー
        $this->patch('/api/update_user_name', [])->assertStatus(422);

        // 成功
        $this->patch('/api/update_user_name', ['name' => 'Takashi'])->assertStatus(200);
    }
}
```

`patch`でリクエストして、エラーだと422を想定して`assertStatus(422)`してます。
これを実行すると、

```sh
$ vendor/bin/phpunit tests/Feature/UpdateUserNameApiTest.php 
PHPUnit 8.5.0 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 383 ms, Memory: 18.00 MB

There was 1 failure:

1) Tests\Feature\UpdateUserNameApiTest::testApi
Expected status code 422 but received 302.
Failed asserting that false is true.

apisample/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:183
apisample/tests/Feature/UpdateUserNameApiTest.php:19

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
```

422じゃなくて302だよと言われ、テストが失敗してしまいました。

よくよく考えると、このテストで実行しているリクエストはAJAXリクエストではありません。
なので422ではなくて、リダイレクトの302が返ってくるわけです。

それじゃあ、実際の運用と同じようにAPIにリクエストするにはどうしたらよいかというと、公式ドキュメントにしっかり載っていました。

https://laravel.com/docs/6.x/http-tests#testing-json-apis

```tests/Feature/UpdateUserNameApiTest.php 
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class UpdateUserNameApiTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testApi()
    {
        // エラー
        $this->patchJson('/api/update_user_name', [])->assertStatus(422);

        // 成功
        $this->patchJson('/api/update_user_name', ['name' => 'Takashi'])->assertStatus(200);
    }
}
```

```sh
$ vendor/bin/phpunit tests/Feature/UpdateUserNameApiTest.php 
PHPUnit 8.5.0 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 112 ms, Memory: 18.00 MB

OK (1 test, 2 assertions)
```

公式ドキュメントをちゃんと読みましょう自分:innocent: