Laravel で FromRequest のバリデーションをテストする

Laravel の FormRequest クラスを利用すると各フィールドのバリデーションルールを array の形で定義することができます。

```php
<?php

namespace App\Http\Requests\UserRegistration;

use Illuminate\Foundation\Http\FormRequest;

class UserRegistrationFormRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'min:2',
                'regex:/^[A-Za-z0-9]+$/',
            ],
            'password' => [
                'required',
                'string',
                'min:8',
                'regex:/[a-z]/',
                'regex:/[A-Z]/',
                'regex:/[0-9]/',
                'regex:/@$!%*#?&/',
            ],
        ];
    }
}
```

一つ一つのルールをテストするメリットは特にないと思いますが、上記のような複数ルールの組み合わせやカスタムバリデーションを検証するために Unit Test を作成する際の Tips を紹介します。

## テストコード

ベースとなるメソッドは [Stack Overflow](https://stackoverflow.com/questions/36978147/unit-test-laravels-formrequest) から拝借しています（というか、この記事の要点はこの方法の紹介です！）。
これをベースに汎用的な FormRequest 用の TestCase クラスを作ってみます。

```php
<?php

namespace Tests\Unit\App\Requests\Base;

use Illuminate\Foundation\Testing\TestCase;

abstract class BaseFormRequestTestCase extends TestCase
{
    /**
     * @var array
     */
    protected $rules;
    /**
     * @var \Validator
     */
    protected $validator;

    public function setUp(): void
    {
        parent::setUp();
        $this->setRules();
        $this->validator = $this->app['validator'];
    }

    /**
     * 子クラスで Request Class が持つ rules を set する。
     */
    abstract protected function setRules(): void;

    protected function getFieldValidator($field, $value)
    {
        return $this->validator->make(
            [$field => $value],
            [$field => $this->rules[$field]]
        );
    }

    protected function validateField($field, $value)
    {
        return $this->getFieldValidator($field, $value)->passes();
    }
}
```

これを継承した形で TestCase を作成すれば比較的シンプルにバリデーションのテストを書くことができます。

```php
<?php

namespace Tests\Unit\App\Requests\UserRegistration\UserRegistrationFormRequest;

use App\Http\Requests\UserRegistration\UserRegistrationFormRequest;
use Tests\Unit\App\Requests\Base\BaseFormRequestTestCase;

class UserRegistrationFormRequestTest extends BaseFormRequestTestCase
{
    protected function setRules(): void
    {
        $this->rules = (new UserRegistrationFormRequest())->rules();
    }

    public function testNameRules() {
        $this->assertFalse($this->validateField('name', null));
        $this->assertFalse($this->validateField('name', 'a'));
        $this->assertTrue($this->validateField('name', 'ab'));
        $this->assertTrue($this->validateField('name', 'AB'));
        $this->assertTrue($this->validateField('name', '01'));
        $this->assertTrue($this->validateField('name', 'aA0'));
        $this->assertFalse($this->validateField('name', 'ab@'));
        $this->assertFalse($this->validateField('name', 'ab cd'));
    }

    public function testPasswordRules() {
        $this->assertFalse($this->validateField('password', null));
        $this->assertFalse($this->validateField('password', 'abAB01@'));
        $this->assertTrue($this->validateField('password', 'abAB01@$'));
        $this->assertFalse($this->validateField('password', 'abcABC012'));
        $this->assertFalse($this->validateField('password', 'ABC012@$!'));
        $this->assertFalse($this->validateField('password', '012@$!abc'));
        $this->assertFalse($this->validateField('password', '@$!abcABC'));
        $this->assertFalse($this->validateField('password', 'ab AB 01 @$'));
    }
}

```