📌

FormRequestのUnitテストの書き方

に公開

概要

最近私の開発チームで、LaravelでFormRequestのテストをどう書くのかについて考える機会があったので、FormRequestのテストの書き方について記事にしてみました。

環境

Laravel 9.52.9
PhpUnit 9.6.8

テスト対象のFormRequest

例えば、TestRequestというFormRequestがあるとします。

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TestRequest extends FormRequest
{
    public function rules()
    {
        return [
            'title' => 'required|string|max:255',
            'body' => 'required|string',
        ];
    }
}

テストを書く

バリデーションを通過するか否かをテストする

tests/Unit/Request ディレクトリに TestRequestTest.php ファイルを作成します。
まずは、複数ケースの入力値とバリデーションの成否の期待値を与えて、バリデーションを通過するかどうかを検証するテストを作成します。

複数ケースの入力値、期待値のデータの準備には、dataProviderを使用します。

namespace Tests\Unit;

use App\Http\Requests\TestRequest;
use Illuminate\Support\Facades\Validator;
use PHPUnit\Framework\TestCase;

class TestRequestTest extends TestCase
{
    /**
     * @group validation
     * @dataProvider provideTestData
     */
    public function test_バリデーションの有無をテスト($input, $expected)
    {
        $request = new TestRequest();
        $validator = Validator::make($input, $request->rules());
        $this->assertEquals($expected, $validator->passes());
    }
    
    /**
     * バリデーションテスト用データプロバイダー
     * 1要素目: 入力値
     * 2要素目: バリデーションの結果(true:成功/false:失敗)
     */
    public function provideTestData()
    {
        return [
            '正常' => [
                [
                    'title' => 'test title',
                    'body' => 'test body'
                ],
                true
            ],
            'bodyなしエラー' => [
                [
                    'title' => 'test title',
                ],
                false
            ],
            'titleなしエラー' => [
                [
                    'body' => 'test body',
                ],
                false
            ]
        ];
    }
}

テストの実行を行います。

php artisan test --group=validation

   PASS  Tests\Unit\Request\TestRequestTest
 validation with data set "正常"                                                                                                       0.32s
 validation with data set "bodyなしエラー"                                                                                                      0.02s
 validation with data set "titleなしエラー"                                                                                                      0.02s
  Tests:    3 passed (3 assertions)
  Duration: 0.56s

個別のルールを検証する

上記のテストでは、バリデーションを通過するかどうかについては検証できますが、1つ1つのルールが適用されているかを個別に検証するのが困難です。例えば、バリデーションで検証するキーが増えた際に、特定のキーのrequiredの検証を行いたい場合に、それ以外のすべてのキーが存在する入力値を準備する必要がありとても手間になってしまいます。

そこで、下記のようにすることで個別のルールを検証できるようにします。

    /**
     * @group validation
     * @dataProvider provideTestData
     */
    public function testValidation($target, $input, $expected, $message = '', $exceptMessage = ''): void
    {
        $request = new TestRequest();
        $validator = Validator::make($input, $request->rules());
        $errors = $validator->messages();
        $this->assertEquals($expected, $errors->has($target));
    }
    
    /**
     * 1要素目: 検証する対象のキー名
     * 2要素目: 入力値
     * 3要素目: 対象キーのバリデーションエラー有無(true:有/false:無)
     */
    public function provideTestData()
    {
        return [
            'title_ok' => [
                'title',
                [
                    'title' => 'test title'
                ],
                false,
            ],
            'title_ng' => [
                'title',
                [],
                true,
            ],
            'body_ok' => [
                'body',
                [
                    'body' => 'test body'
                ],
                false,
            ],
            'body_ng' => [
                'body',
                [],
                true,
            ]
        ];
    }

テストを実行します。

php artisan test --group=validation

   PASS  Tests\Unit\Request\TestRequestTest
 validation with data set "title ok"                                                                                                 0.47s
 validation with data set "title ng"                                                                                                 0.02s
 validation with data set "body ok"                                                                                                  0.02s
 validation with data set "body ng"                                                                                                  0.02s

  Tests:    4 passed (4 assertions)
  Duration: 0.95s

まとめ

LaravelでFormRequestのテストを書く方法をまとめてみました。
まずは、FormRequestのバリデーションが通過するか否かをテストする方法について書いてみました。
しかし、FormRequestで検証するキーが多い場合に上記の方法だとテストの記述量が膨大になってしまうため、それを解消するための「キーそれぞれを検証するテスト」についても記述しました。
もっとこうしたら良くなるよというご意見あればウェルカムですのでお気軽にコメントください。
誰かのお役に立てば幸いです。

Discussion