FormRequestのサブクラスに対するUnitTest雛形


掲題の通りです。

# テスト
ポイントとしては、

- フォーム全体のテストではなく、要素ごとのテストになっている（バリデーション全体の結果は見ずに、各要素ごとの結果しだけassertしている）
- データベース系のテストが書きやすいように、第二引数はクロージャを渡せるようにしてある

参考：
https://qiita.com/n_mogi/items/57a946205df2a69889c2


~~~ php
    /**
     * バリデーション項目に対するテスト
     * 1つのフォーム要素に対する1回の入力を1テストケースとして設計。
     * @param $item テスト対象の要素名
     * @param $data 入力値。クロージャもOK（主にfactory() 使用のため）
     * @param $expect 期待するバリデーション結果。true:バリデーションOK　false:バリデーションNG
     * @dataProvider dataProviderTestValidateEachAttribute
     */
    public function testValidateEachAttribute($item, $data, $expect)
    {
        //テスト対象のフォームリクエストクラスのインスタンス
        $request = new SubClassOfFormRequest();

        //クロージャの場合は、クロージャの実行結果をテスト値として代入
        if (is_object($data) && $data instanceof \Closure) {
            $value = call_user_func($data);
        } else {
            $value = $data;
        }
        //テスト対象のパラメタ作成
        $dataList = [$item => $value];

        //ルール取得
        $rules = $request->rules();

        //Validatorファサードでバリデーターインスタンスを取得、その際に入力情報とバリデーションルールを引数で渡す
        /** @var CustomValidator $validator */
        $validator = Validator::make($dataList, $rules);

        //バリデーション実行(バリデーションが走るタイミングよくわからないが、一度passes()を叩かないとfailed()の結果が取れないっぽい)
        $validator->passes();

        //バリデーション失敗した場合、failed()結果の配列に、テスト対象の$itemのエラー内容が含まれている。
        $exists = array_key_exists($item, $validator->failed());

        //配列の中に存在していなければバリデーション合格
        $actual = !$exists;

        //テスト結果メッセージの生成
        $message = "item:" . $item . " data:" . print_r($data, true) . " expected:" . $expect;

        //assertion
        $this->assertEquals($expect, $actual, $message);
    }

~~~

# データプロバイダ
テストケースのサンプルです。
わりと適当

~~~ php
    /**
     * データプロバイダ
     * @return array
     */
    public function dataProviderTestValidateEachAttribute()
    {
        return [
            //パラメタp_idに対するテスト
            "パラメタp_idのテスト.文字列のためエラーとなる" => ["p_id", "a", false],
            "パラメタp_idのテスト.空文字列のためエラーとなる" => ["p_id", "", false],
            "パラメタp_idのテスト.小数のためエラーとなる" => ["p_id", "0.123", false],
            "パラメタp_idのテスト.配列のためエラーとなる" => ["p_id", [1, 2], false],
            "パラメタp_idのテスト.0はエラー" => ["p_id", 0, false],
            "パラメタp_idのテスト.null" => ["p_id", null, false],
            "パラメタp_idのテスト.smallintの最大値オーバー" => ["p_id", 32768, false],
            "パラメタp_idのテスト.smallintの最大値、商品IDが存在しないためエラー" => ["p_id", 32767, false],

            "パラメタpのテスト.非表示の商品IDのため、エラー" => [
                "p_id",
                function () {
                    /** @var Product $product */
                    $product = factory(Product::class, "invisible")->create();
                    return $product->id;
                },
                false
            ],

            "パラメタpのテスト.整数（正常系）" => [
                "p",
                function () {
                    /** @var Product $product */
                    $product = factory(Product::class, "visible")->create();
                    return $product->id;
                },
                true
            ],

            //パラメタhogeに対するテスト
            "パラメタhogeのテスト.整数（正常系）" => ["hoge", "1", true],
            "パラメタhogeのテスト.整数（正常系）" => ["hoge", "2", true],
            "パラメタhogeのテスト.整数（正常系）" => ["hoge", "3", true],
            "パラメタhogeのテスト.0はエラー" => ["hoge", "0", false],
            "パラメタhogeのテスト.範囲外の整数、エラー" => ["hoge", "4", false],
            "パラメタhogeのテスト.先頭0の整数ライクな文字列、エラー" => ["hoge", "00", false],
            "パラメタhogeのテスト.先頭0の整数ライクな文字列、エラー" => ["hoge", "01", false],
            "パラメタhogeのテスト.先頭0の整数ライクな文字列、エラー" => ["hoge", "02", false],
            "パラメタhogeのテスト.空文字列（正常系：空欄可）" => ["hoge", "", true],
            "パラメタhogeのテスト.範囲外の整数、エラー" => ["hoge", "-1", false],
            "パラメタhogeのテスト.文字列のためエラー" => ["hoge", "a", false],
            "パラメタhogeのテスト.小数のためエラー" => ["hoge", "1.23", false],
            "パラメタhogeのテスト.配列のためエラー" => ["hoge", [1, 2], false],
            "パラメタhogeのテスト.null(正常系:空欄可)" => ["hoge", null, true],

        ];
    }
~~~