⚖️

FormRequest のテストでエラーメッセージをアサーションする

ふわせぐ2021/09/03に公開

はじめに

適切なバリデーションメッセージを返してくれているのかをテストする方法が,ぱっとググった感じ出てこなかったので書いておこうと思います.

FormRequest とは

Laravel には,Controller で受け取るリクエストのバリデーションを行う仕組みとして FormRequest というものがあります.
FormRequest は,複雑なバリデーションロジックを書くことができ,非常に便利な機能です.

Check 👉 フォームリクエストバリデーション

例えば,FormRequest では以下のようにバリデーションルールを定義します.

/**
 * リクエストに適用するバリデーションルールを取得
 *
 * @return array
 */
public function rules()
{
    return [
        'user_id' => 'required|alpha_num',
        'birthday' => 'required|date',
    ];
}

この例だと,user_id は必須入力で,英数字以外 を許可しません.また,birthday も必須入力で,日付以外 を許可しません.
ここに,バリデーション失敗時に JsonResponse を返すようにしてみます.以下の処理を同じクラス配下に追加します.

/**
 * バリデーションエラーが起きたら実行される
 *
 * @param Validator $validator
 * @return HttpResponseException
 */
protected function failedValidation(Validator $validator): HttpResponseException
{
    $response = response()->json([
        'status' => 'validation error',
        'errors' => $validator->errors()
    ], 400);
    throw new HttpResponseException($response);
}

こうすることで,バリデーションに引っかかった場合,何がいけなかったのか,エラーメッセージ付きでレスポンスを返してくれるようになりました.

FormRequest のテストを書く

さて,ここからが本題になります.
まず,いろんな記事に載っている一般的なテストを示します.これは,バリデーションが正常に通るかどうかだけをテストするものです.
なお,今回テストを行う,FormRequest を継承するクラスは UserFormRequest とします.

/**
 * [正常系] バリデーションが通る
 *
 * @return void
 */
public function test__正常系_バリデーションが通る()
{
    $requestParams = [
        'user_id' => 'fuwasegu2021',
        'birthday' => '1998-08-04'
    ];

    $request = new UserFormRequest(); // インスタンスを生成
    $rules = $request->rules(); // バリデーションルールを取得

    /** @var \Illuminate\Validation\Validator */
    $validator = Validator::make($requestParams, $rules); // ダミーデータをバリデーションに通す
    $result = $validator->passes(); // チェックが通ったかどうかを取得する

    $this->assertTrue($result); // $result が trle ならテストが通る
}

ではさらに,実際に吐かれたメッセージの取得と,期待されるバリデーションメッセージとの比較を行うテストを追加します.
今回は,user_id が入力されなかった時のテストを書いてみます.この場合,期待されるエラーメッセージは

The user id field is required.

となります.

/**
 * [異常系] user_id が空
 *
 * @return void
 */
public function test__異常系_user_id_が空()
{
    $requestParams = [
        'user_id' => '',
        'birthday' => '1998-08-04'
    ];

    $request = new UserFormRequest(); // インスタンスを生成
    $rules = $request->rules(); // バリデーションルールを取得

    /** @var \Illuminate\Validation\Validator */
    $validator = Validator::make($requestParams, $rules); // ダミーデータをバリデーションに通す
    
    $actualMessages = $validator->messages()->get('user_id'); // 実際のバリデーションメッセージを取得
    $expectedMessage = 'The user id field is required.'; // 期待するバリデーションメッセージ

    $this->assertSame( // 期待するメッセージと実際のメッセージを比較する
        $expectedMessage,
        $actualMessages[\array_search($expectedMessage, $actualMessages, true)]
    );
}

解説

まずはこの行.

$actualMessages = $validator->messages()->get('user_id');

messages() メソッドは,Illuminate\Validation\Validator クラスのメソッドで,Illuminate\Support\MessageBag インスタンスを返します.こいつにバリデーションメッセージたちが詰まっているわけです.
そして,get() メソッドは,Illuminate\Support\MessageBag クラスのメソッドで,引数で指定された key に格納されているメッセージを全て取得します.従って,get() の戻り値は 配列 になります.この key は,バリデーションで引っかかったパラメータ名になりますので,今回は user_id です.

次にこの行

$actualMessages[\array_search($expectedMessage, $actualMessages, true)]

$actualMessages は配列ですので,array_search() を使って期待するバリデーションメッセージを含んでいるかを調べます.この時,第3引数に true を渡すことで厳密な評価が行われます.
array_sartch() は配列の key を返しますので,改めて取得した key を使って $actualMessages からメッセージを取得し,期待するバリデーションメッセージと比較を行うわけです.

assertSame() を使うことで,期待していたものとは違うメッセージが出力された場合に,Expected Actual をコンソール上で確認できるので,assertTrue() を使うより便利です.
また,assertSame()assertEquals() より厳密な評価を行うので,特に理由がなければ前者を使うべきでしょう.

まとめ

  • バリデーションエラーのメッセージは,messages()get() で取得できる
  • 取得したエラーは配列で返ってくるので,array_search() で該当するエラーメッセージを取得する
  • アサーションは assertTrue() を使うよりも assertSame() を使う方が,期待していた結果と実際の結果が見えるのでテスト時に嬉しい
GitHubで編集を提案
株式会社ゆめみ

みんな知ってるあのサービスも、ゆめみが一緒に作ってます。スマホアプリ/Webサービスの企画・UX/UI設計、開発運用の内製化支援。Swift,Kotlin,Rust,Go,Flutter,ML,React,AWS等エンジニア・クリエイターの会社です。Twitterで情報配信中

Discussion

ログインするとコメントできます