実務でPHPUnitを使ってValidationの異常系テストコードを追加することがあったので
そこで得られた知識をアウトプットします!
仮に以下のRequestファイルがあったとします。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max:100',
'email' => 'required'
];
}
}
ユーザー名に必須チェックと最大文字数
メールアドレスに必須チェック
のバリデーションがついています。
テストコードの作成
バリデーションのチェックは、一つ一つチェックしていくこともできますが、
コードの保守性を考え、データプロバイダーを使用しました。
<?php
class UserRequestTest extends TestCase
{
/* *
* バリデーションテスト
*
* @param array 項目名の配列
* @param array 値の配列
* @param 期待値(true:バリデーションOK、false:バリデーションNG)
*
* 使用するデータプロバイダーを記述
* @dataProvider dataprovider
*/
public function testBasicTest(array $keys, array $values, bool $expect): void
{
//入力項目の配列($keys)と値の配列($values)から、連想配列を生成する
$dataList = array_combine($keys, $values);
//先ほど作成したUserRequestのオブジェクトを作成
$request = new UserRequest();
//ユーザーリクエストで定義したルールを取得
$rules = $request->rules();
//Validatorファサードでバリデーターのインスタンスを取得、その際に入力情報とバリデーションルールを引数で渡す
$validator = Validator::make($dataList, $rules);
//入力情報がバリデーショルールを満たしている場合はtrue、満たしていな場合はfalseが返る
$result = $validator->passes();
//期待値($expect)と結果($result)を比較
$this->assertEquals($expect, $result);
}
/**
* データプロバイダ
*
* @return データプロバイダ
*
* @dataProvider dataprovider
*/
public function dataprovider(): array
{
return [
'ユーザー名が必須エラー' => [
['name', 'email'],
[null, 'test@example.com'],
false
],
'ユーザー名が100文字超えているエラー' => [
['name', 'email'],
[str_repeat('あ', 101),'test@example.com'],
false
],
'メールアドレスが必須エラー' => [
['name', 'email'],
['test', null],
false
],
];
}
}
今回は異常系のテストだけを記述しましたが、
このように記述することで、複数のバリデーションでも見やすく、変更がしやすいテストを記述することができます!
余談
プロバイダーメソッドを使用してつまづいた実装をメモで残しておきます。
つまづいた点①
ネストされた値をどうチェックするか
user.nameのようにドットで項目が記載されている
仮に以下のRequestファイルがあったとします。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'user.name' => 'required|max:100',
'user.email' => 'required'
];
}
}
今まで通りにデータプロバイダーを作成しても、正しい動きになりません。
実際に何時間も悩みました...
上記の場合は、以下のように書くと正しく動きます!
/**
* データプロバイダ
*
* @return データプロバイダ
*
* @dataProvider dataprovider
*/
public function dataprovider(): array
{
return [
'ユーザー名が必須エラー' => [
['user',],
[['name' => null], ['email' => 'test@example.com']],
false
],
'ユーザー名が100文字超えているエラー' => [
['name', 'email'],
[['name' => str_repeat('あ', 101)], ['email' => 'test@example.com']],
false
],
'メールアドレスが必須エラー' => [
['name', 'email'],
[['name' => 'test'], ['email' => null]],
false
],
];
}
つまづいた点②
ログインユーザーIDが使われている
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'id' => $this->user()->id,
'name' => 'required|max:100',
'email' => 'required'
];
}
}
上記の例は、認証などが関わっており、そこで使用されていました。
この場合も、データプロバイダーに
Auth::id()
と書いても正しい動きにはなりません。
このような場合は、以下のように書く必要があります。
<?php
class UserRequestTest extends TestCase
{
/* *
* バリデーションテスト
*
* @param array 項目名の配列
* @param array 値の配列
* @param 期待値(true:バリデーションOK、false:バリデーションNG)
*
* 使用するデータプロバイダーを記述
* @dataProvider dataprovider
*/
public function testBasicTest(array $keys, array $values, bool $expect): void
{
//入力項目の配列($keys)と値の配列($values)から、連想配列を生成する
$dataList = array_combine($keys, $values);
//先ほど作成したUserRequestのオブジェクトを作成
$request = new UserRequest();
// setUserResolverを呼ぶ
$request = setUserResolver(function () use ($user) {
return $user;
}
//ユーザーリクエストで定義したルールを取得
$rules = $request->rules();
//Validatorファサードでバリデーターのインスタンスを取得、その際に入力情報とバリデーションルールを引数で渡す
$validator = Validator::make($dataList, $rules);
//入力情報がバリデーショルールを満たしている場合はtrue、満たしていな場合はfalseが返る
$result = $validator->passes();
//期待値($expect)と結果($result)を比較
$this->assertEquals($expect, $result);
}
}
のようにしてあげると正しい挙動になる。
あくまで、自分の学習のメモとして残しているので、参考程度に見ていただけると幸いです。