Expected response status code [201] but received 404.Failed asserting that 201 is identical to 404.

## 開発環境

- Mac OS Monterey（バージョン12.2.1）
- MacBook Air （M1）
- composer version 2.0.9
- Laravel Framework 10.10
- phpunit 10.1

HTTPリクエストのテストコードを実装したので、以下のコマンドを実行。

```php
php artisan test tests/Feature/APITest.php
```

すると、以下のようなエラーが！！！

```php
Expected response status code [201] but received 404.
Failed asserting that 201 is identical to 404.
```

201のステータスコードを期待したが、404を受け取っているらしい。

中々苦戦したが、下記を試すことによりテストを通すことができたのでよかったら参考にしてください。

※ちなみに今回は、テストコード用のDBを作成していないので、localを使用しています。

## 今回使用したコード

```php
Route::middleware(['middleware' => 'web'])->group(function () {
    //todo作成
    Route::get('/todos', [PostController::class, 'create']);
    Route::post('/todos', [PostController::class, 'create']);
...
});
```

```php
class APITest extends TestCase
{
    public function test_HTTPリクエスト()
    {
				$response = $this->postJson('/web/todos');
        $response->assertCreated();
    }
}
```

## 試したこと

今回試したこととしては、以下になります。

- エラー内容の確認をして、RouteServiceProvider.phpの中身を変更
- VerifyCsrfToken.phpのファイルの書き換え

## エラー内容の確認をして、RouteServiceProvider.phpの中身を変更

とりあえず、以下のエラーを再確認。

```php
Expected response status code [201] but received 404.Failed asserting that 201 is identical to 404.     

     21▕     // }
     22▕     public function test_トップ()
     23▕     {
     24▕         $response = $this->postJson('/web/todos');
  ➜  25▕         $response->assertCreated();
     26▕     }
     27▕ }
     28▕ 
     29▕ // class ControllerTest extends TestCase
```

エラー箇所を確認していて、1つ思ったのが、PostControllerでは`Route::get('/todos')`としているのに、テストコードの中では、`/web/todos`としている点。

webをつけていた理由としては、RouteServiceProvider.phpのmidleware(’web’)の定義のprefixで、webを指定していたのでつけていたのだが、postControllerの中では`/todos`としているので、とりあえず、prefixを削除。

```php
public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });

        $this->routes(function () {
            Route::middleware('api')
                ->namespace($this->namespace)
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->prefix('web')//←これを削除
                ->group(base_path('routes/web.php'));
        });
    }
```

また、テストコードの中も、`/todos`とした。

再度`php artisan test tests/Feature/APITest.php`のコマンドを実行したところ…

```php
Expected response status code [200] but received 419.
Failed asserting that 200 is identical to 419.
```

エラーが変わった！！！

## VerifyCsrfToken.phpのファイルの書き換え


このエラーを調べるとどうやらセッションエラーらしく、エラー内容確認のため以下をテストコードに追加。

```php
class APITest extends TestCase
{
    public function test_HTTPリクエスト()
    {
				$this->withoutExceptionHandling();//←追加
				$response = $this->postJson('/todos');
        $response->assertCreated();
    }
}
```

CSRFが一致していないというエラーであることがわかった。

```php
CSRF token mismatch.
```

postController上でmiddlewareを使用していることからCSRFの機能が提供されているので、テストコード上では無効にしたい。

そこで、ファイルの内容を以下のように変更。

```php
...
public function handle($request, \Closure $next)
    {
        if (env('APP_ENV') !== 'local') {//←この部分はAPP_ENVに合わせる
            return parent::handle($request, $next);
        }

        return $next($request);
    }
```

ちなみに、公式ドキュメントにもCSRFは無効にしようの文言がありました。

> Tip!! 利便性を良くするため、テストの実行時にCSRFミドルウェアを自動で無効にします。
> 

[HTTPテスト 10.x Laravel](https://readouble.com/laravel/10.x/ja/http-tests.html)

再度`php artisan test tests/Feature/APITest.php`のコマンドを実行

```php
Expected response status code [201] but received 200.
Failed asserting that 201 is identical to 200.
```

おお、エラーの内容がまた変わった！

そして、200番台になっているので、サーバー側のエラーは無くなったと思われる。

APITestの中身を確認し、`assertCreated()`を調べてみる。

```php
class APITest extends TestCase
{
    public function test_HTTPリクエスト()
    {
				$this->withoutExceptionHandling();//←追加
				$response = $this->postJson('/todos');
        $response->assertCreated();//←ここを変更
    }
}
```

どうやら、`assertCreated`は201を返すらしく、ここでは200を使用したいので`assertOk`に変更。

これでようやく…

```php
PASS  Tests\Feature\APITest
  ✓ HTTPリクエスト                                                                                                                                              0.12s  

  Tests:    1 passed (1 assertions)
  Duration: 0.18s
```

テストコードが通りました〜

## 参考


https://c-a-p-engineer.github.io/tech/2022/11/01/laravel-phpunit-419/

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

https://zenn.dev/naoki_oshiumi/articles/54ae6f85784ba6