0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

バリデーションエラーは302リダイレクトを返すのが「あたりまえ体操」だった件について

Last updated at Posted at 2024-01-05

はじめに

テストを書いている際、バリデーションエラーを返すスクリプトを書いているはずなのに、なぜかテストをパスしてしまうことがありました。

その理由は何でしょうか?

有識者の方は、症例を見ながら是非推理していただければと思います。

環境

Laravel 9.52.16

問題

  • 以下のようなコントローラのアクションをテストしたい
SalesOrdersController.php(一部略)
public function store(Request $request)
    {
        // バリデーション
        $request->validate([
            'title' => 'required|max:2000',
            'sales_abstract' => 'required|max:2000',
            'category' => 'required|max:50',
            'budget' => 'required|numeric',
            'schedule' => 'required|date',
        ]);

        // 認証済みユーザの発注として作成(リクエストされた値を元に作成)
        $request->user()->sales_orders()->create([
            'title' => $request->title, 
            'sales_abstract' => $request->sales_abstract,
            'category' => $request->category,
            'budget' => $request->budget,
            'schedule' => $request->schedule,
        ]);

        // 前のURLへリダイレクトさせる
        return back();
    }

そこで最初に次のようなテストコード(失敗例)を書きました。

SalesOrdersControllerTest.php(一部略)
public function test_sales_store(): void
    {
        // テスト用のユーザを作成
        $user = User::factory()->create();

        // 作成したユーザで認証
        $response = $this->actingAs($user)->post('/my/sales/store');

        $response->assertStatus(302);
    }

しかし、上記テスト内ではpostメソッドにフォームデータを渡していません。

先ほどのコントローラ内にはバリデーションがあるので、フォームデータがなければバリデーションエラーを返すはずです。

つまり自分はこのテストが通らず、エラーを返すことを期待していました。

Tinker上
root:/var/www/html/niches# php artisan test --filter test_sales_store

Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0

Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0

   PASS  Tests\Feature\SalesOrdersControllerTest
  ✓ sales store

  Tests:  1 passed
  Time:   9.81s

しかしなぜか上のようにテストが通ってしまい「???」となっていました。

バリデーションエラーは発生しなかったということでしょうか?

解決法(答え)

  • 実際にはバリデーションエラーは発生していた
  • しかしバリデーションエラーもreturn back()と同じく、302リダイレクトを返す
  • そのためassertStatus(302)が通った
  • よってテストが通った

バリデーションエラー時の動作

①リダイレクト
バリデーションルールに違反すると、Laravelはユーザをフォームがあった元のページにリダイレクトします。ユーザがフォームを修正して再度送信できるようにするためです。

②エラーメッセージの表示
そしてリダイレクトともに、「パスワードは半角英数字16字以内で入力してください」といったエラーメッセージが表示されます。

③ステータスコード
そしてこのリダイレクトはHttpステータスコード「302」を伴います。

つまり自分が書いたテストコードは、成功しても失敗してもパスされるような構造になってしまっていました。

この場合の正しいテストコード

SalesOrdersControllerTest.php
public function test_sales_store(): void
{
    $user = User::factory()->create();

    $formData = [
        'title' => 'テストタイトル',
        'sales_abstract' => 'テスト概要',
        'category' => 'テストカテゴリー',
        'budget' => 1000,
        'schedule' => '2023-01-01',
    ];

    $response = $this->actingAs($user)->post('/my/sales/store', $formData);

    $response->assertStatus(302);
    $this->assertDatabaseHas('sales_orders', ['title' => 'テストタイトル']);
}

まずはフォームデータを用意し、postメソッドに引数として渡します。

そして最後に「$this->assertDatabaseHas('sales_orders', ['title' => 'テストタイトル']);」と書いてあげることで、

正しくフォームデータがデータベースに保存されているか、テストすることができます。

まとめ

よく考えてみたら、自分も日常生活の中でパスワードを設定する時にバリデーションエラーを起こす時があります。

そしてその時には、必ずパスワードを設定する画面に戻されていました。

今回の経験は「内部的にはこうなってるんや」と考える良い時間になりました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?