Laravel のバリデーションの After フックをテストする

zumikiti

March 20, 2024

以下のようなバリデーションがあったとする。 after フックを単体テストを行いたい場合の備忘録。

<?php

class UserFrom extends FormRequest
{
    public function rules()
    {
        return [
            'first_name' => 'required',
            'last_name'  => 'required',
        ];
    }

    public function withValidate($validator)
    {
        $validator->after(function($validator) {
            $this->userNameValidate($validator);
        });
    }

    /**
     * ユーザー名が空白文字含めて51文字いないかのチェック
     */
    private function userNameValidate($validator)
    {
        $firstName = $this->input('first_name');
        $lastName = $this->input('last_name');

        $userName = "{$lastName} {$firstName}";

        $count = mb_strlen($userName);

        if ($count > 51) {
           $validator->errors()->add(
               'last_name',
               'ユーザー名は、50文字以内で入力してください。'
            );
        }
    }
}
<?php

class UserFormTest extends TestCase
{
    private $request;

    public function roles()
    {
        // after フックを起動させるためにリクエストをモックする必要がある
        $this->request = new UserFrom();
        $this->request->setRouteResolver(function() {
            $mock = Mockery::mock('Illuminate\Routing\Route')
                ->shouldReceive('parameter')
                ->andReturn()
                ->getMock();

            return $mock;
        }); 

        $rules = $this->request->rules();

        return $rules;
    }

    private function makeInput(array $input = [], string $exceptKey = null)
    {
        $baseInput = [
            'first_name' => Str::random(25),
            'last_name' => Str::random(25),
        ];

        // input の値を置き換え
        $input = array_replace($baseInput, $input);
        // input のキーから指定のキーを抜く
        $input = Arr::except($input, $exceptKey);

        return $input;
    }

    /**
     * @dataProvider formData
     */
    public function testFormValidation($expected, ...$inputs)
    {
        $rules = $this->rules();
        $input = $this->makeInput(...$inputs);

        // モックした rules をセット
        $validator = Validator::make($input, $rules);
        $this->request->merge($input);
        // withValidator() に $validator を渡すことで after フックを単体テストで実行できる
        $this->request->withValidator($validator);

        $result = $validator->passes();
        $this->assertSame($expected, $result);
    }

    public function formData()
    {
        return [
            'all OK' => [
                true,
            ],
            'first_name が存在しない' => [
                false,
                [],
                'first_name',
            ],
            'ユーザー名が51文字' => [
                false,
                [
                    'first_name' => Str::random(26),
                ],
            ],
        ];
    }
}

なお、編集などで URI にパラメータが必要な場合は、request の生成時に以下の用に parameter() をセットすれば良い。

<?php

$user = UserFactory::new()->create();

$mock = Mockery::mock('Illuminate\Routing\Route')
    ->shouldReceive('parameter')
    ->parameter('user', $user) // ここ
    ->andReturn()
    ->getMock();