学習として自動でFormRequestクラスとそれに対するテストを作成できるものをつくってみました[Laravel]

##目的
手動で入力テストをするのが、手間なのでExcelファイルの中身をymlファイルにまとめて、FormRequestとそれに対するテストを作成したいです(学習にちょうどいいと思った・・・)

初心者なので、いろいろミスがあると思いますが、コメントなどで指摘していただけると助かります。

##作るもの
・Excelをymlファイルにまとめる
・ymlファイルからFormRequestを作る
・ymlファイルからテストを作る

<b>コマンドでパパっとそれっぽくかっこいいので、コマンドで作れるようにする。</b>

###今回の構成

```
		Command --- MakeExcelToRequestYml.php //ExcelをYmlファイルに変化するコマンドクラス
		         |
		         -- MakeRequestYmlToClass.php //ymlファイルをフォームリクエストクラスにするコマンドクラス
		         |
		         -- MakeRequestYmlToTest.php //ymlファイルをフォームリクエストに対するテストを作るコマンドクラス
		         
		Facades --- FileFacade.php //ファイル操作の共通クラス
		         |
		         -- DirectoryFacade.php //ディレクトリ操作の共通クラス    
		         
		config --- validation.php //テスト項目     
	
		resources - views - templates --- request_yml_stub.blade.php //ymlファイルを作るための仮blade
		                               |
		                               -- request_class_stub.blade.php //FormRequestクラスを作るための仮blade
		                               |
		                               -- request_test_stub.blade.php //FormRequestに対応したtestを作る仮blade
		                               
		storage - Requests --- Excel - ymlをつくるためのExcelファイルを置くディレクトリ
		                    |
		                    -- Classes - 作成したFormRequestクラスを置くディレクトリ
		                    |
		                    -- Tests - 作成したテストを置くディレクトリ
```

##準備編
今回は、目的が違うので、環境構築は飛ばします。

###laravelのインストール

```:コマンド
composer create-project --prefer-dist laravel/laravel request-generator
```

ymlとexcelを扱う必要があるので、それを扱いやすくする「Laravel Excel」とSymfonyのYaml Componentをインストールします。

```:コマンド
composer require maatwebsite/excel
composer require symfony/yaml
```

これで準備が整いました。

##config/validation.phpの作成(テスト項目)

よく使うルールをテストできれば、とりあえずいいので、よく使うルールに対しての項目を記述していく。
valueが期待値になっています。[同値分割と境界値に関しての記事はこちらを参考](https://qiita.com/softest/items/648d8bb4021cd1256b02)
一応期待値通りに動くのか、テストは行いました。(大丈夫そうでした)
境界値はkeyが-1,0,1となっているのは、指定された制限に-1,0,1を加えるためにそうしています。

```:validation.php
<?php

return [

    //同値分割
    'equivalentDivision' => [

        //文字列
        'string' => [
            'test'  => true,
            'てすと' => true,
            '.'     => true,
            10      => false
        ],

        //整数値
        'integer' => [
            'test' => false,
            10     => true,
            '10.1' => false,
            -10    => true
        ],

        //数値
        'numeric' => [
            'test' => false,
            10     => true,
            '10.1' => true,
            -10    => false
        ],

        //日付
        'date' => [
            'test'                 => false,
            '1-1-1 1:1:1'          => true,
            '11/01/01 01:01:01'    => true,
            '111/11/11 11:11:11'   => false,
            '1111/11/11 11:11:11'  => true,
            '11111/11/11 11:11:11' => false,
            '1111/111/11 11:11:11' => false,
            '1111/11/111 11:11:11' => false,
            '1111/11/11 111:11:11' => false,
            '1111/11/11 11:111:11' => false,
            '1111/11/11 11:11:111' => false,
            '11111111'             => true,
            '11111111 11:11:11'    => true,
            '11111111 111111'      => true,
            '11111111 11/11/11'    => false,
            '1111111111:11:11'     => true,
        ],

        //メール
        'email' => [
            '10@co.jp'       => true,
            'test@gmail.com' => true,
            '$@softbank.jp'  => true,
            '.@softbank.jp'  => false,
            '$softbank.jp'   => false,
            '$@@softbank.jp' => false,
            '$@yahoo.co.jp'  => true,
            '$@test'         => true,
            '$@10'           => true,
            '$@$'            => true,
            '$@.'            => false,
        ],

        //半角英数記号
        'regex:/^[!-~]+$/' => [
            'test'  => true,
            'ｔｅｓｔ'  => false,
            'てすと' => false,
            '.'     => false,
            '。'     => false,
            '10'    => false,
            '１０'    => false,
        ],

        //半角英小文字大文字数字をそれぞれ1種類以上含む
        'regex:/\A(?=.?[a-z])(?=.?[A-Z])(?=.*?\d)[a-zA-Z\d]+\z/' => [
            'aA1' => true,
            'A1'  => false,
            'a1'  => false,
            'aA'  => false,
        ],
    ],

    //境界値
    'boundaryValue' => [

        //最大　　数値もしくは文字数　　(...etc ファイルサイズなど)
        'max' => [
            -1 => true,
            0  => true,
            1  => false,
        ],

        //最小　　数値もしくは文字数　　(...etc ファイルサイズなど)
        'min' => [
            -1 => false,
            0  => true,
            1  => true,
        ],

        //数値　最小～最大  (....etc ファイルサイズ・文字サイズなど)
        'between' => [
            'min' => [
                -1 => false,
                0  => true,
                1  => true,
            ],
            'max' => [
                -1 => true,
                0  => true,
                1  => false,
            ],
        ],

        //数値桁数
        'digits' => [
            -1 => false,  //1桁の場合、0桁になるのでテストしない
            0  => true,
            1  => false,
        ],

        //数値桁数 最小～最大
        'digits_between' => [
            'min' => [
                -1 => false, //1桁の場合、0桁になるのでテストしない
                0  => true,
                1  => true,
            ],
            'max' => [
                -1 => true,
                0  => true,
                1  => false,
            ],
        ]
    ],

    //存在有無
    'exists' => [

        //必須
        'required' => [
            '' => false
        ],

        //nullable
        'nullable' => [
            '' => true
        ],
    ]

];
```

##Facadesを作る

###DirectoryFacade.php(ディレクトリ操作の共通クラス)
今回はすべて、storage/Requests配下に作ることを前提としています。
<b>本当は、だんだんとfacadeに処理をまとめたのですが、今回はそれだと長くなりすぎるため、ここでfacadeを一気に書いています
(実際はだんだんまとめていったほうが早く書けると思う)</b>

```:DirectoryFacade.php
<?php
namespace App\Facade;

use Illuminate\Support\Facades\File;

class DirectoryFacade
{
    const EXCEL    = 'Excel';
    const REQUESTS = 'Requests';
    const CLASSES  = 'Classes';
    const TESTS    = 'Tests';

    static function makeDirectoryInRequests($directoryPath)
    {
        //storage/Requests配下に固定
        $storageDirPath = storage_path(self::REQUESTS. '/'. $directoryPath);

        if (!File::exists($storageDirPath)){
            File::makeDirectory($storageDirPath, 0777, true);
        }
    }
}
```

<b>・makeDirectoryInRequests：Requestsディレクトリ配下に指定されたpathのディレクトリを作る

###FileFacade.php(ファイル操作の共通クラス)
今回はすべて、storage/Requestsのそれぞれのディレクトリ配下に作ることを前提としています。

```:FileFacade.php
<?php
namespace App\Facade;

use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;

class FileFacade
{
    const REQUEST_YML         = 'request.yml';
    const STUB_REQUEST_YML    = 'request_yml_stub';
    const STUB_REQUEST_CLASS  = 'request_class_stub';
    const STUB_REQUEST_TEST   = 'request_test_stub';
    const DIRECTORY_TEMPLATES = 'templates';

    static function makeFileInRequests($fileName, $fileContent, $directoryPath = null)
    {
        $storageFilePath = self::getStorageFilePathInRequests($fileName, $directoryPath);
        File::put($storageFilePath, $fileContent);
    }

    static function makeContent($viewName, $contentData, $isTemplate = true)
    {
        if ($isTemplate){
            $viewName = self::DIRECTORY_TEMPLATES. ".{$viewName}";
        }

        $content = view($viewName, $contentData)->render();
        return $content;
    }

    static function existsFileInRequests($fileName, $directoryPath = null)
    {
        $storagePath = self::getStorageFilePathInRequests($fileName, $directoryPath);

        if (!File::exists($storagePath)){
            echo "{$fileName}は存在しません。";
            exit;
        }
    }

    static function getFilesInRequests($directoryPath)
    {
        $storagePath = storage_path(DirectoryFacade::REQUESTS. '/'. $directoryPath);
        return File::files($storagePath);
    }

    static function getYamlFileInRequests($fileName, $directoryPath = null)
    {
        return Yaml::parse(self::getFileInRequests($fileName, $directoryPath));
    }

    static function getFileInRequests($fileName, $directoryPath = null)
    {
        $storagePath = self::getStorageFilePathInRequests($fileName, $directoryPath);
        return File::get($storagePath);
    }

    private static function getStorageFilePathInRequests($fileName, $directoryPath = null)
    {
        $filePath = $fileName;
        if (isset($directoryPath))$filePath = Str::start($filePath, "{$directoryPath}/");
        $filePath =  Str::start($filePath, DirectoryFacade::REQUESTS. '/');

        //storage配下に固定
        return storage_path($filePath);
    }
}
```

<b>・getStorageFilePathInRequests：Requst配下の対象のパスを返す</b>
<b>・getFileInRequests：Requst配下の対象のパスのファイルの中身を返す</b>
<b>・getYamlFileInRequests：Requst配下の対象のパスのymlファイルの中身を返す</b>
<b>・getFilesInRequests：Requst配下の対象のパスのファイル一覧を返す</b>
<b>・existsFileInRequests：Requst配下の対象のパスのファイルが存在するか調べる</b>
<b>・makeContent：templates/配下のstubファイルに変数をわたして、そのファイルの中身を読み込み違うファイルに書く中身を作成</b>
<b>・makeFileInRequests：Requst配下の対象のパスにファイルを作成する</b>

##実装編

##①Excelをymlファイルにまとめるものをつくる

###ymlにするExcelファイルをつくる

ファイル名はなんでもいいようにします。(ただしExcelファイル)
画像のように、特定の場所にクラス名、あとはフィールドとそれに対するlaravelのバリデーションルールを設定する
今回は、FormRequestクラスごとにシートを変えて記述する想定

<img width="780" alt="Screenshot_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325600/38ce1d39-cd17-f4d5-045f-b76663fc03a3.png">
<img width="713" alt="Screenshot_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325600/015f90e5-e211-bac6-8c47-6ff3ea96394f.png">

###request_yml_stub.blade.php(ymlファイルを作るための仮blade)を作成します。

[ymlの詳しい記法はこちら](https://qiita.com/tfrcm/items/5f8e4c5795ce41b214d1)

```
"クラス名" => array:3 [
    "フィールド名" => array:3 [
      0 => "ルール"
      1 => "ルール"
      2 => "ルール"
    ]
    "フィールド名" => array:3 [
      0 => "ルール"
      1 => "ルール"
      2 => "ルール"
    ]
  ]
```

扱いやすそうな形でとれるymlの形を目指します。これならそのあとの扱いが簡単そうなので、こちらに合わせてymlのフォーマットを作ります。

```:request_yml_stub.blade.php
@foreach($requestYmlData as $className => $fieldDefine)
{{$className}} :
@foreach($fieldDefine as $fieldName => $ruleList)
  {{$fieldName}} :
@foreach($ruleList as $rule)
@if(!isset($rule))@continue @endif
    - {{$rule}}
@endforeach
@endforeach

@endforeach
```

＠foreachはタブを開けると、ymlを作成したときにずれが生じるので一番左に固定しています。これでrequestYmlDataを渡せばうまくいきそうです。

###MakeExcelToRequestYml.php(ExcelをYmlファイルに変化するコマンドクラス)を作成する

```:コマンド
php artisan make:command MakeExcelToRequestYml
```

```:MakeExcelToRequestYml.php
<?php

namespace App\Console\Commands;

use App\Facade\DirectoryFacade;
use App\Facade\FileFacade;
use Illuminate\Console\Command;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;

class MakeExcelToRequestYml extends Command
{
    protected $signature   = 'make:request-yml';
    protected $description = 'Excelを読み込んで、Requestクラスの設定を定義するYmlファイルを作成する。';

    private $excelExcludeRowNumberList = [0, 1, 2];
    private $excelExcludeColNumberList = [0, 1, 2];
    private $excelFieldRowNumber       = 2;
    private $classNamePosition         = ['col' => 1, 'row' => 2];


    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $requestYmlData    = $this->getRequestYmlData();
        $requestYmlContent = FileFacade::makeContent(FileFacade::STUB_REQUEST_YML, ['requestYmlData' => $requestYmlData]);

        FileFacade::makeFileInRequests(FileFacade::REQUEST_YML, $requestYmlContent);

        echo 'request.ymlを作成しました。';
    }

    /**
     * request.ymlに定義するデータを取得
     * @return array
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     */
    private function getRequestYmlData()
    {
        $excelAllSheets = $this->loadFirstExcelAllSheets();

        $requestYmlData = [];
        foreach ($excelAllSheets as $sheet){

            $excelData = $sheet->toArray();
            $className = $excelData[$this->classNamePosition['col']][$this->classNamePosition['row']];

            $fieldRuleList = collect($excelData)->diffKeys($this->excelExcludeRowNumberList)->values();

            $tempFieldList = [];
            foreach ($fieldRuleList as $fieldRule){
                if (!isset($fieldRule[$this->excelFieldRowNumber]))continue;

                $fieldName = $fieldRule[$this->excelFieldRowNumber];
                $ruleList  = collect($fieldRule)->diffKeys($this->excelExcludeColNumberList)->values();

                $tempFieldList[$fieldName] = $ruleList;
            }

            $requestYmlData[$className] = $tempFieldList;
        }

        return $requestYmlData;
    }

    /**
     * Excelファイル(バリデーションルール)の読み込み
     * @return \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet[]
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     */
    private function loadFirstExcelAllSheets()
    {
        $reader = new Xlsx();
        $files = FileFacade::getFilesInRequests(DirectoryFacade::EXCEL);

        if (count($files) !== 1){
            echo 'storage/Requests/Excel配下にファイルが存在しません。もしくは、複数ファイルが存在しています。';
            exit;
        }

        $spreadsheet = $reader->load($files[0]);
        return $spreadsheet->getAllSheets();
    }
}
```

<b>・loadFirstExcelAllSheets:Excelディレクトリ内の最初のExcelファイルのすべてのシートを読み込む</b>
<b>・getRequestYmlData:request_yml_stub.blade.phpに渡すためのデータを成型して取得</b>
<b>・handle:コマンド実行時に実行される(今回、Excelからymlファイルを作成してくれる)

さっそく実行してみます。

```:コマンド
php artisan make:request-yml
request.ymlを作成しました。
```

####出力結果request.yml

```:request.yml
TestRequest1 :
  id1 :
    - required
    - integer
    - between:1,5
  id2 :
    - required
    - integer
    - digits:5
  id3 :
    - required
    - regex:/^[!-~]+$/

TestRequest2 :
  id1 :
    - required
    - max:20
  id2 :
    - required
    - integer
    - min:5
    - max:9
  id3 :
    - required
    - email
  id4 :
    - required
    - string
  id5 :
    - required
    - integer
  id6 :
    - required
    - date

```

狙い通りの記述になりました。

##②ymlファイルをフォームリクエストクラスにする

### request_class_stub.blade.php(FormRequestクラスを作るための仮blade)

```:request_class_stub.blade.php
@php
    $phpTag = '<?php';
@endphp
{!! $phpTag !!}

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class {{$className}} extends FormRequest
{
    public function rules()
    {
        $rules = [
@foreach($fieldDefine as $fieldName => $ruleList)
            '{{$fieldName}}' => '{{implode('|', $ruleList)}}',
@endforeach
        ];

        return $rules;
    }

    public function authorize()
    {
        return true;
    }

    public function messages()
    {
        return [];
    }
}

```

request_class_stub.blade.phpは受け取ったフィールドごとにルールを設定しています。

###MakeRequestYmlToClass.php(ymlファイルをフォームリクエストクラスにするコマンドクラス)

```:コマンド
php artisan make:command MakeRequestYmlToClass
```

```:MakeRequestYmlToClass.php
<?php

namespace App\Console\Commands;

use App\Facade\DirectoryFacade;
use App\Facade\FileFacade;
use Illuminate\Console\Command;

class MakeRequestYmlToClass extends Command
{
    protected $signature   = 'make:request-class';
    protected $description = 'request.ymlを参考に、フォームリクエストクラスを作成する';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        FileFacade::existsFileInRequests(FileFacade::REQUEST_YML);

        DirectoryFacade::makeDirectoryInRequests(DirectoryFacade::CLASSES);

        $requestDefine = FileFacade::getYamlFileInRequests(FileFacade::REQUEST_YML);
        $this->makeRequestClasses($requestDefine);

        echo 'request.ymlを参考に、フォームリクエストクラスを作成しました。';
    }

    /**
     * FormRequestクラスの作成
     * @param $requestDefine
     */
    private function makeRequestClasses($requestDefine)
    {
        foreach ($requestDefine as $className => $fieldDefine) {

            $tempContent = FileFacade::makeContent(FileFacade::STUB_REQUEST_CLASS, [
                'className'   => $className,
                'fieldDefine' => $fieldDefine
            ]);

            $tempFilename = "{$className}.php";
            FileFacade::makeFileInRequests($tempFilename, $tempContent, DirectoryFacade::CLASSES);
        }
    }
}
```

<b>・makeRequestClasses:ymlのデータを受け取り、それに基づいてFormRequestクラスを作成する</b>

実行します。

```:コマンド
 php artisan make:request-class
request.ymlを参考に、フォームリクエストクラスを作成しました。
```

Tests配下に二つのFormRequestクラスが来ました。

```:TestRequest1.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TestRequest1 extends FormRequest
{
    public function rules()
    {
        $rules = [
            'id1' => 'required|integer|between:1,5',
            'id2' => 'required|integer|digits:5',
            'id3' => 'required|regex:/^[!-~]+$/',
        ];

        return $rules;
    }

    public function authorize()
    {
        return true;
    }

    public function messages()
    {
        return [];
    }
}
```

```:TestRequest2.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TestRequest2 extends FormRequest
{
    public function rules()
    {
        $rules = [
            'id1' => 'required|max:20',
            'id2' => 'required|integer|min:5|max:9',
            'id3' => 'required|email',
            'id4' => 'required|string',
            'id5' => 'required|integer',
            'id6' => 'required|date',
        ];

        return $rules;
    }

    public function authorize()
    {
        return true;
    }

    public function messages()
    {
        return [];
    }
}

```

うまくいきました。

##③ymlファイルをフォームリクエストに対するテストを作る

###request_test_stub.blade.php(FormRequestに対応したtestを作る仮blade)

```:request_test_stub.blade.php
@php
    use App\Console\Commands\MakeRequestYmlToTest;

        $phpTag = '<?php';
@endphp
{!! $phpTag !!}

namespace Tests\Requests;

use Tests\TestCase;
use App\Http\Requests\{{$requestClass}};
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;

class {{$className}} extends TestCase
{
    /**
     * @dataProvider dataProviderValidation
     */
    public function testValidation($testData, $expect)
    {
        $request = new {{$requestClass}}();
        $rules   = $request->rules();

        $validator = Validator::make($testData, $rules);
        $result    = $validator->passes();

        $this->assertEquals($expect, $result);

        $exportBool = ($expect) ? 'true' : 'false';
        Log::info('test:{{$className}}|targetClass:{{$requestClass}}|data:'. json_encode($testData). '|expect:'. $exportBool);
    }

    public function dataProviderValidation()
    {
        return [
@foreach($testPattern as $testData)
@if(empty($testData))@continue @endif
@foreach($testData as $key => $fieldPattern)
            '{{$key}}' => [
                [
@foreach($fieldPattern[MakeRequestYmlToTest::KEY_FIELD_VALUE_LIST] as $fieldName => $fieldValue)
                    '{{$fieldName}}' => @if(is_string($fieldValue))'{{$fieldValue}}'@else{{$fieldValue}}@endif,
@endforeach
                ],
                {{var_export($fieldPattern[MakeRequestYmlToTest::KEY_EXPECT])}}
            ],
@endforeach
@endforeach
        ];
    }
}
```

<b>・dataProviderを使うと、その配列をテスト実行したときに順番に入れてくれる(＠dataProvider dataProviderValidationで指定する必要がある)</b>
<b>・testValidation：フォームリクエストクラスのバリデーションをmakeし、それにdataproviderのデータを入れて、テストをして、入力値などをLogに保存している

###MakeRequestYmlToTest.php

<b>構想としては、一つのすべてのバリデーションをクリアするパターンを作成して、そこのテストする値だけを上書きして、それをdataprovider用に配列にまとめる。また、境界値のルールが指定されているものは同値分割のほうのテストを行わない、なぜなら例えば、string:max5でstringをもとに同値分割のテストをすると、現在の入力条件(validation.php)だと5文字以下のものがあり、エラーになってしまう(これは動的にする必要がありそう境界値・同値分割両方行うために)</b>

```:コマンド
php artisan make:command MakeRequestYmlToTest
```

```:MakeRequestYmlToTest.php
<?php

namespace App\Console\Commands;

use App\Facade\DirectoryFacade;
use App\Facade\FileFacade;
use Illuminate\Console\Command;
use Illuminate\Support\Str;

class MakeRequestYmlToTest extends Command
{
    protected $signature   = 'make:request-test';
    protected $description = 'request.ymlを参考に、リクエストテストを作成する';

    const DELIMITER_COLON  = ':'; //バリデーションルールサイズ区切り文字「:]
    const DELIMITER_COMMA  = ','; //バリデーションルールサイズ区切り文字「,]

    const WORD_TRUE  = 'true'; //true
    const WORD_FALSE = 'false'; //false

    //バリデーションルール
    const RULE_TYPE_STRING         = 'string';
    const RULE_TYPE_INTEGER        = 'integer';
    const RULE_TYPE_NUMERIC        = 'numeric';
    const RULE_TYPE_BETWEEN        = 'between';
    const RULE_TYPE_DIGITS         = 'digits';
    const RULE_TYPE_DIGITS_BETWEEN = 'digits_between';

    //境界値の際、サイズを考慮する必要がある型
    const INCLUDE_SIZE_TYPE_LIST = [
        self::RULE_TYPE_STRING,
        self::RULE_TYPE_INTEGER,
        self::RULE_TYPE_NUMERIC,
    ];

    //型がbetween
    const INCLUDE_BETWEEN_TYPE_LIST = [
        self::RULE_TYPE_BETWEEN,
        self::RULE_TYPE_DIGITS_BETWEEN,
    ];

    const KEY_FIELD_VALUE_LIST = 'fieldValueList';
    const KEY_EXPECT           = 'expect';

    private $configValidation;   //テストパターン設定ファイル内容
    private $equivalentDivision; //同値分割
    private $boundaryValue;      //境界値
    private $exists;             //存在有無

    private $equivalentRuleTypeList; //同値分割型ルール
    private $boundaryRuleTypeList;   //境界値型ルール
    private $existsRuleTypeList;   //存在有無ルール

    public function __construct()
    {
        $this->configValidation   = config('validation');
        $this->equivalentDivision = collect($this->configValidation['equivalentDivision']);
        $this->boundaryValue      = collect($this->configValidation['boundaryValue']);
        $this->exists             = collect($this->configValidation['exists']);

        $this->equivalentRuleTypeList = $this->equivalentDivision->keys();
        $this->boundaryRuleTypeList   = $this->boundaryValue->keys();
        $this->existsRuleTypeList     = $this->exists->keys();

        parent::__construct();
    }

    public function handle()
    {
        FileFacade::existsFileInRequests(FileFacade::REQUEST_YML);

        DirectoryFacade::makeDirectoryInRequests(DirectoryFacade::TESTS);

        $requestDefine = FileFacade::getYamlFileInRequests(FileFacade::REQUEST_YML);
        $this->makeRequestTest($requestDefine);

        echo 'request.ymlを参考に、リクエストテストを作成しました。';
    }

    /**
     * クラスごとにテストファイルを作製する
     * @param $requestDefine
     */
    private function makeRequestTest($requestDefine)
    {
        foreach ($requestDefine as $RequestClassName => $fieldDefine){

            $tempPassPattern = $this->makePassValidationPattern($fieldDefine);
            $tempTestPattern = $this->makeTestPattern($fieldDefine, $tempPassPattern);

            $tempTestClassName = "{$RequestClassName}Test";
            $tempContent = FileFacade::makeContent(FileFacade::STUB_REQUEST_TEST, [
                'className'    => $tempTestClassName,
                'requestClass' => $RequestClassName,
                'testPattern'  => $tempTestPattern
            ]);

            $tempFilename = "{$tempTestClassName}.php";
            FileFacade::makeFileInRequests($tempFilename, $tempContent, DirectoryFacade::TESTS);
        }
    }

    /**
     * バリデーションを通過するパターンを作成
     * @param $fieldDefine
     * @return array
     */
    private function makePassValidationPattern($fieldDefine)
    {
        $passPattern = [];
        foreach ($fieldDefine as $fieldName => $ruleList) {

            $equivalentRuleType = $this->equivalentRuleTypeList->intersect($ruleList)->first();

            //ruleType=nullの場合デフォルトstring
            if (!isset($equivalentRuleType))$equivalentRuleType = self::RULE_TYPE_STRING;

            $inputList          = collect($this->equivalentDivision[$equivalentRuleType]);
            $trueInputValueList = $inputList->filter()->keys();

            $passPattern[$fieldName] = $trueInputValueList->first();

            //境界値のルールが設定されてたら検討する
            $boundaryRuleType = collect($ruleList)->diff($this->existsRuleTypeList)->diff([$equivalentRuleType])->first();
            if (!isset($boundaryRuleType))continue;

            $size = Str::after($boundaryRuleType, self::DELIMITER_COLON);

            //betweenの場合
            if (Str::contains($size, self::DELIMITER_COMMA)){
                $size = Str::before($size, self::DELIMITER_COMMA);
            }

            //digitsの場合
            if (Str::contains($boundaryRuleType, self::RULE_TYPE_DIGITS)){
                $passPattern[$fieldName] = $this->makeDigitsValue(0, $size);
                continue;
            }

            $passPattern[$fieldName] = $this->makeAmountValue(0, $size, $equivalentRuleType);
        }

        return $passPattern;
    }

    /**
     * バリデーションにかけるパターンを作成
     * @param $fieldDefine
     * @param $passPattern
     * @return array
     */
    private function makeTestPattern($fieldDefine, $passPattern)
    {
        $testPattern = [];
        foreach ($fieldDefine as $fieldName => $ruleList){

            $ruleType = $this->equivalentRuleTypeList->intersect($ruleList)->first();

            foreach ($ruleList as $rule){

                //存在テスト
                $tempTestPattern = $this->makeExistsPattern($rule, $fieldName, $passPattern);
                $testPattern[] = $tempTestPattern;
                if (count($tempTestPattern) > 0)continue;

                //sizeルールを含んでいる場合、同値分割はしない
                $boundaryRule = collect($ruleList)->diff($this->equivalentRuleTypeList)->diff($this->existsRuleTypeList)->first();
                if (isset($boundaryRule) && in_array($rule, $this->equivalentRuleTypeList->toArray(), true)){
                    continue;
                }

                //「：」がなければ同値分割
                $tempTestPattern = $this->makeEquivalentDivisionPattern($rule, $fieldName, $passPattern);
                $testPattern[] = $tempTestPattern;
                if (count($tempTestPattern) > 0)continue;

                //境界値
                $tempTestPattern = $this->makeBoundaryValuePattern($rule, $ruleType, $fieldName, $passPattern);
                $testPattern[] = $tempTestPattern;
            }
        }

        return $testPattern;
    }

    /**
     * 存在パターン
     * @param $rule
     * @param $fieldName
     * @param $passPattern
     * @return array
     */
    private function makeExistsPattern($rule, $fieldName, $passPattern)
    {
        $existsPattern = [];
        if (!$this->existsRuleTypeList->contains($rule))return $existsPattern;

        $inputPatternList = $this->exists[$rule];

        foreach ($inputPatternList as $inputValue => $expect){

            $tempInputPattern = $this->makeInputPattern($passPattern, $fieldName, $inputValue, $expect);

            $exportBool = ($expect) ? self::WORD_TRUE : self::WORD_FALSE;
            $existsPattern["fieldName:{$fieldName},rule:{$rule},inputValue:{$inputValue},expect:{$exportBool}"] = $tempInputPattern;
        }

        return $existsPattern;
    }

    /**
     * 同値分割の入力データ作成
     * @param $rule
     * @param $fieldName
     * @param $passPattern
     * @return array
     */
    private function makeEquivalentDivisionPattern($rule, $fieldName, $passPattern)
    {
        $equivalentDivisionPattern = [];
        if (Str::contains($rule, self::DELIMITER_COLON))return $equivalentDivisionPattern;

        $inputPatternList = $this->equivalentDivision[$rule];
        foreach ($inputPatternList as $inputValue => $expect){

            $tempInputPattern = $this->makeInputPattern($passPattern, $fieldName, $inputValue, $expect);

            $exportBool = ($expect) ? self::WORD_TRUE : self::WORD_FALSE;
            $equivalentDivisionPattern["fieldName:{$fieldName},rule:{$rule},inputValue:{$inputValue},expect:{$exportBool}"] = $tempInputPattern;
        }

        return $equivalentDivisionPattern;
    }

    /**
     * 境界値の入力データ作成
     * @param $ruleIncludeSize
     * @param $ruleType
     * @param $fieldName
     * @param $passPattern
     * @return array
     */
    private function makeBoundaryValuePattern($ruleIncludeSize, $ruleType, $fieldName, $passPattern)
    {
        $boundaryValuePattern = [];
        if (!in_array($ruleType, self::INCLUDE_SIZE_TYPE_LIST, true))return $boundaryValuePattern;

        $sizeRule = Str::before($ruleIncludeSize, self::DELIMITER_COLON);
        $size     = Str::after($ruleIncludeSize, self::DELIMITER_COLON);
        $boundaryList = $this->boundaryValue[$sizeRule];

        //betweenを使っている場合はもう一段階深い
        $boundaryValuePattern = $this->makeBetweenPattern($sizeRule, $size, $ruleType, $boundaryList, $passPattern, $fieldName);
        if (count($boundaryValuePattern) > 0)return $boundaryValuePattern;

        $boundaryValuePattern = $this->makeDigitsPattern($boundaryList, $passPattern, $fieldName, $size,  $sizeRule);
        if (count($boundaryValuePattern) > 0)return $boundaryValuePattern;

        $boundaryValuePattern = $this->makeAmountValuePattern($boundaryList, $passPattern, $fieldName, $size, $ruleType, $sizeRule);
        return $boundaryValuePattern;
    }

    /**
     * ruleがbetweenの場合のパターン
     * @param $sizeRule
     * @param $minMaxSize
     * @param $ruleType
     * @param $boundaryMaxMin
     * @param $passPattern
     * @param $fieldName
     * @return array
     */
    private function makeBetweenPattern($sizeRule, $minMaxSize, $ruleType, $boundaryMaxMin, $passPattern, $fieldName)
    {
        $boundaryValuePattern = [];
        if (!in_array($sizeRule, self::INCLUDE_BETWEEN_TYPE_LIST, true))return $boundaryValuePattern;

        $min = Str::before($minMaxSize, self::DELIMITER_COMMA);
        $max = Str::after($minMaxSize, self::DELIMITER_COMMA);

        foreach ($boundaryMaxMin as $maxMinRule => $boundaryList){
            $size = ${$maxMinRule};

            $tempPattern = $this->makeDigitsPattern($boundaryList, $passPattern, $fieldName, $size, $sizeRule);
            $boundaryValuePattern = array_merge($boundaryValuePattern, $tempPattern);
            if (count($tempPattern) > 0)continue;

            $tempPattern = $this->makeAmountValuePattern($boundaryList, $passPattern, $fieldName, $size, $ruleType, $sizeRule);
            $boundaryValuePattern = array_merge($boundaryValuePattern, $tempPattern);
        }

        return $boundaryValuePattern;
    }

    /**
     * ルールが桁数の場合のパターン
     * @param $boundaryList
     * @param $passPattern
     * @param $fieldName
     * @param $size
     * @param $sizeRule
     * @return array
     */
    private function makeDigitsPattern($boundaryList, $passPattern, $fieldName, $size, $sizeRule)
    {
        $boundaryValuePattern = [];
        if (!Str::contains($sizeRule, self::RULE_TYPE_DIGITS)) return $boundaryValuePattern;

        foreach ($boundaryList as $addNumber => $expect){

            //テストする項目のみ入力値を上書き
            $overWriteValue = $this->makeDigitsValue($addNumber, $size);

            $tempInputPattern = $this->makeInputPattern($passPattern, $fieldName, $overWriteValue, $expect);

            $exportBool = ($expect) ? self::WORD_TRUE : self::WORD_FALSE;
            $boundaryValuePattern["fieldName:{$fieldName},rule:{$sizeRule}: {$size},inputValue:{$tempInputPattern['fieldValueList'][$fieldName]},expect:{$exportBool}"] = $tempInputPattern;
        }

        return $boundaryValuePattern;
    }

    /**
     * ルールが桁数の場合の値
     * @param $addNumber
     * @param $size
     * @return string
     */
    private function makeDigitsValue($addNumber, $size)
    {
        $digits     = $size + $addNumber;
        $inputValue = str_pad(1, $digits, 1, STR_PAD_LEFT);

        return $inputValue;
    }

    /**
     * 文字数・数値パターン
     * @param $boundaryList
     * @param $passPattern
     * @param $fieldName
     * @param $size
     * @param $ruleType
     * @param $sizeRule
     * @return array
     */
    private function makeAmountValuePattern($boundaryList, $passPattern, $fieldName, $size, $ruleType, $sizeRule)
    {
        $boundaryValuePattern = [];
        foreach ($boundaryList as $addNumber => $expect){

            //テストする項目のみ入力値を上書き
            $overWriteValue = $this->makeAmountValue($addNumber, $size, $ruleType);

            $tempInputPattern = $this->makeInputPattern($passPattern, $fieldName, $overWriteValue, $expect);

            $exportBool = ($expect) ? self::WORD_TRUE : self::WORD_FALSE;
            $boundaryValuePattern["fieldName:{$fieldName},rule:{$sizeRule}: {$size},inputValue:{$tempInputPattern['fieldValueList'][$fieldName]},expect:{$exportBool}"] = $tempInputPattern;
        }

        return $boundaryValuePattern;
    }

    /**
     * 文字数・数値
     * @param $addNumber
     * @param $size
     * @param $ruleType
     * @return float|int|string
     */
    private function makeAmountValue($addNumber, $size, $ruleType)
    {
        //整数値の場合(数値)
        $inputValue = $size + $addNumber;
        if ($ruleType === self::RULE_TYPE_INTEGER)return $inputValue;

        //数値の場合(数値)(小数にする)
        $inputValue = $size + ($addNumber / 10);
        if ($ruleType === self::RULE_TYPE_NUMERIC)return $inputValue;

        //文字列の場合(何も指定がない・もしくはstringの場合は文字列)
        $inputValue = Str::random($size + $addNumber);

        return $inputValue;
    }

    /**
     * 成功パターンのテスト項目を上書きしたパターンを返す(実際のテスト値)
     * @param $passPattern
     * @param $fieldName
     * @param $overWriteValue
     * @param $expect
     * @return mixed
     */
    private function makeInputPattern($passPattern, $fieldName, $overWriteValue, $expect)
    {
        //バリデーションを通過するパターン
        $inputPattern[self::KEY_FIELD_VALUE_LIST] = $passPattern;

        //テストする項目のみ入力値を上書き
        $inputPattern[self::KEY_FIELD_VALUE_LIST][$fieldName] = $overWriteValue;

        $inputPattern[self::KEY_EXPECT] = $expect;

        return $inputPattern;
    }
}
```
<b>・makeRequestTest：クラスごとにテストファイルを作製する</b>
<b>・makePassValidationPattern：バリデーションを通過するパターンを作成</b>
<b>・makeTestPattern：バリデーションにかけるパターンを作成</b>
<b>・makeExistsPattern：存在パターンの作成</b>
<b>・makeEquivalentDivisionPattern：同値分割の入力データ作成</b>
<b>・makeBoundaryValuePattern：境界値の入力データ作成</b>
<b>・makeBetweenPattern：ruleがbetweenの場合のパターン</b>
<b>・makeDigitsPattern：ルールが桁数の場合のパターン</b>
<b>・makeDigitsValue：ルールが桁数の場合の値</b>
<b>・makeAmountValuePattern:文字数・数値パターン作成</b>
<b>・makeAmountValue:文字数・数値作成</b>
<b>・makeInputPattern:成功パターンのテスト項目を上書きしたパターンを返す(実際のテスト値)</b>

コマンド実行

```:コマンド
php artisan make:request-test
request.ymlを参考に、リクエストテストを作成しました。
```

####TestRequest1Test

```:TestRequest1Test.php
<?php

namespace Tests\Requests;

use Tests\TestCase;
use App\Http\Requests\TestRequest1;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;

class TestRequest1Test extends TestCase
{
    /**
     * @dataProvider  dataProviderValidation
     */
    public function testValidation($testData, $expect)
    {
        $request = new TestRequest1();
        $rules   = $request->rules();

        $validator = Validator::make($testData, $rules);
        $result    = $validator->passes();

        $this->assertEquals($expect, $result);

        $exportBool = ($expect) ? 'true' : 'false';
        Log::info('test:TestRequest1Test|targetClass:TestRequest1|data:'. json_encode($testData). '|expect:'. $exportBool);
    }

    public function dataProviderValidation()
    {
        return [
            'fieldName:id1,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '',
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id1,rule:between: 1,inputValue:0,expect:false' => [
                [
                    'id1' => 0,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id1,rule:between: 1,inputValue:1,expect:true' => [
                [
                    'id1' => 1,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                true
            ],
            'fieldName:id1,rule:between: 1,inputValue:2,expect:true' => [
                [
                    'id1' => 2,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                true
            ],
            'fieldName:id1,rule:between: 5,inputValue:4,expect:true' => [
                [
                    'id1' => 4,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                true
            ],
            'fieldName:id1,rule:between: 5,inputValue:5,expect:true' => [
                [
                    'id1' => 5,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                true
            ],
            'fieldName:id1,rule:between: 5,inputValue:6,expect:false' => [
                [
                    'id1' => 6,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id2,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => 1,
                    'id2' => '',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id2,rule:digits: 5,inputValue:1111,expect:false' => [
                [
                    'id1' => 1,
                    'id2' => '1111',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id2,rule:digits: 5,inputValue:11111,expect:true' => [
                [
                    'id1' => 1,
                    'id2' => '11111',
                    'id3' => 'test',
                ],
                true
            ],
            'fieldName:id2,rule:digits: 5,inputValue:111111,expect:false' => [
                [
                    'id1' => 1,
                    'id2' => '111111',
                    'id3' => 'test',
                ],
                false
            ],
            'fieldName:id3,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => 1,
                    'id2' => '11111',
                    'id3' => '',
                ],
                false
            ],
        ];
    }
}
```

####TestRequest2Test

```:TestRequest2Test.php
<?php

namespace Tests\Requests;

use Tests\TestCase;
use App\Http\Requests\TestRequest2;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;

class TestRequest2Test extends TestCase
{
    /**
     * @dataProvider  dataProviderValidation
     */
    public function testValidation($testData, $expect)
    {
        $request = new TestRequest2();
        $rules   = $request->rules();

        $validator = Validator::make($testData, $rules);
        $result    = $validator->passes();

        $this->assertEquals($expect, $result);

        $exportBool = ($expect) ? 'true' : 'false';
        Log::info('test:TestRequest2Test|targetClass:TestRequest2|data:'. json_encode($testData). '|expect:'. $exportBool);
    }

    public function dataProviderValidation()
    {
        return [
            'fieldName:id1,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id2,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => '',
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id2,rule:min: 5,inputValue:4,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 4,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id2,rule:min: 5,inputValue:5,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id2,rule:min: 5,inputValue:6,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 6,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id2,rule:max: 9,inputValue:8,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 8,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id2,rule:max: 9,inputValue:9,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 9,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id2,rule:max: 9,inputValue:10,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 10,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id3,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id3,rule:email,inputValue:10@co.jp,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:test@gmail.com,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => 'test@gmail.com',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:$@softbank.jp,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@softbank.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:.@softbank.jp,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '.@softbank.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id3,rule:email,inputValue:$softbank.jp,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$softbank.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id3,rule:email,inputValue:$@@softbank.jp,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@@softbank.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id3,rule:email,inputValue:$@yahoo.co.jp,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@yahoo.co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:$@test,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@test',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:$@10,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@10',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:$@$,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@$',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id3,rule:email,inputValue:$@.,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '$@.',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id4,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => '',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id4,rule:string,inputValue:test,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id4,rule:string,inputValue:てすと,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'てすと',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id4,rule:string,inputValue:.,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => '.',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id4,rule:string,inputValue:10,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 10,
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id5,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => '',
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id5,rule:integer,inputValue:test,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 'test',
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id5,rule:integer,inputValue:10,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id5,rule:integer,inputValue:10.1,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => '10.1',
                    'id6' => '1-1-1 1:1:1',
                ],
                false
            ],
            'fieldName:id5,rule:integer,inputValue:-10,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => -10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id6,rule:required,inputValue:,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:test,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => 'test',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1-1-1 1:1:1,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1-1-1 1:1:1',
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:11/01/01 01:01:01,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '11/01/01 01:01:01',
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:111/11/11 11:11:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '111/11/11 11:11:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/11/11 11:11:11,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/11/11 11:11:11',
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:11111/11/11 11:11:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '11111/11/11 11:11:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/111/11 11:11:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/111/11 11:11:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/11/111 11:11:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/11/111 11:11:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/11/11 111:11:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/11/11 111:11:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/11/11 11:111:11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/11/11 11:111:11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111/11/11 11:11:111,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111/11/11 11:11:111',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:11111111,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => 11111111,
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:11111111 11:11:11,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '11111111 11:11:11',
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:11111111 111111,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '11111111 111111',
                ],
                true
            ],
            'fieldName:id6,rule:date,inputValue:11111111 11/11/11,expect:false' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '11111111 11/11/11',
                ],
                false
            ],
            'fieldName:id6,rule:date,inputValue:1111111111:11:11,expect:true' => [
                [
                    'id1' => '9SkCIjPFYOvTKiZkVQNO',
                    'id2' => 5,
                    'id3' => '10@co.jp',
                    'id4' => 'test',
                    'id5' => 10,
                    'id6' => '1111111111:11:11',
                ],
                true
            ],
        ];
    }
}
```

##テストを実行するために

・storage/Requests/TestsのTestsディレクトリをtestディレクトリ配下に貼り付ける
・storage/Requests/Classes配下のフォームリクエストクラスをHttp/Requests配下に貼り付ける。

テストを実行する

```:コマンド
./vendor/bin/phpunit tests/Tests
PHPUnit 7.5.14 by Sebastian Bergmann and contributors.

...........................................................       59 / 59 (100%)

Time: 2.96 seconds, Memory: 16.00 MB

OK (59 tests, 59 assertions)
```

完了です。


##作ってみて感じたこと
・どんどんコードが汚くなった(もっと意識たかくしよ)
・validation.phpの中身をもっと増やそ
・正規表現でパイプ「|」を使いたいときに、ルールを|分けしてるから使えない。ruleを配列で指定するようにする
・カスタムルールに対応できない
・編集と追加でFormRequestの中身を微妙に変化させるときに、testが微妙
・ファイルサイズなどは考慮できない(考慮できない)ほかのルールも
・入力テストというよりlaravelのバリデーションルールのテストをしているように感じてしまった。
・境界値のルールが指定されているものは同値分割のほうのテストを行えない、なぜなら例えば、string:max5でstringをもとに同値分割のテストをすると、現在の入力条件(validation.php)だと5文字以下のものがあり、エラーになってしまう(これは動的にする必要がありそう境界値・同値分割両方行うために)

これらの問題を解決できるように、これからもがくしゅうしたいですｎ