開発環境
- Mac OS Monterey(バージョン12.2.1)
- MacBook Air (M1)
- composer version 2.0.9
- Laravel Framework 10.10
- phpunit 10.1
HTTPリクエストのテストコードを実装したので、以下のコマンドを実行。
php artisan test tests/Feature/APITest.php
すると、以下のようなエラーが!!!
Expected response status code [201] but received 404.
Failed asserting that 201 is identical to 404.
201のステータスコードを期待したが、404を受け取っているらしい。
中々苦戦したが、下記を試すことによりテストを通すことができたのでよかったら参考にしてください。
※ちなみに今回は、テストコード用のDBを作成していないので、localを使用しています。
今回使用したコード
Route::middleware(['middleware' => 'web'])->group(function () {
//todo作成
Route::get('/todos', [PostController::class, 'create']);
Route::post('/todos', [PostController::class, 'create']);
...
});
class APITest extends TestCase
{
public function test_HTTPリクエスト()
{
$response = $this->postJson('/web/todos');
$response->assertCreated();
}
}
試したこと
今回試したこととしては、以下になります。
- エラー内容の確認をして、RouteServiceProvider.phpの中身を変更
- VerifyCsrfToken.phpのファイルの書き換え
エラー内容の確認をして、RouteServiceProvider.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を削除。
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
のコマンドを実行したところ…
Expected response status code [200] but received 419.
Failed asserting that 200 is identical to 419.
エラーが変わった!!!
VerifyCsrfToken.phpのファイルの書き換え
このエラーを調べるとどうやらセッションエラーらしく、エラー内容確認のため以下をテストコードに追加。
class APITest extends TestCase
{
public function test_HTTPリクエスト()
{
$this->withoutExceptionHandling();//←追加
$response = $this->postJson('/todos');
$response->assertCreated();
}
}
CSRFが一致していないというエラーであることがわかった。
CSRF token mismatch.
postController上でmiddlewareを使用していることからCSRFの機能が提供されているので、テストコード上では無効にしたい。
そこで、ファイルの内容を以下のように変更。
...
public function handle($request, \Closure $next)
{
if (env('APP_ENV') !== 'local') {//←この部分はAPP_ENVに合わせる
return parent::handle($request, $next);
}
return $next($request);
}
ちなみに、公式ドキュメントにもCSRFは無効にしようの文言がありました。
Tip!! 利便性を良くするため、テストの実行時にCSRFミドルウェアを自動で無効にします。
再度php artisan test tests/Feature/APITest.php
のコマンドを実行
Expected response status code [201] but received 200.
Failed asserting that 201 is identical to 200.
おお、エラーの内容がまた変わった!
そして、200番台になっているので、サーバー側のエラーは無くなったと思われる。
APITestの中身を確認し、assertCreated()
を調べてみる。
class APITest extends TestCase
{
public function test_HTTPリクエスト()
{
$this->withoutExceptionHandling();//←追加
$response = $this->postJson('/todos');
$response->assertCreated();//←ここを変更
}
}
どうやら、assertCreated
は201を返すらしく、ここでは200を使用したいのでassertOk
に変更。
これでようやく…
PASS Tests\Feature\APITest
✓ HTTPリクエスト 0.12s
Tests: 1 passed (1 assertions)
Duration: 0.18s
テストコードが通りました〜
参考