FormRequest のテストでエラーメッセージをアサーションする
はじめに
適切なバリデーションメッセージを返してくれているのかをテストする方法が,ぱっとググった感じ出てこなかったので書いておこうと思います.
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()
を使う方が,期待していた結果と実際の結果が見えるのでテスト時に嬉しい
Discussion