Laravelで書くテスト例(HTTPテスト編)

## 概要
ない頭絞りながら書いたテストの具体例を書きやす。
テスト書いたことないけども、これからテストを書く宿命を背負った人に対して、こんな書き方もあるんだ程度の参考になれれば幸いです。

## 環境
- PHP7.2
- Laravel5.6
- PHPUnit7.0

## テスト対象のコード
バリデーションも含めた記事登録のFeatureテスト(Laravelのドキュメントでいうところの`HTTP Tests`)。
必要な部分しか書いてないです。

### Controller
```php:ArticleController.php
// ファサードに登録してる前提
use ArticleModel;

// バリデーションはLaravelのFormRequestを使用
use App\Http\Requests\StoreArticlePost;

public function storeArticle(StoreArticlePost $request)
{
    $ArticleData = [
        'title'         => $request->input('title'),
        'body'          => $request->input('body'),
        'url'           => $request->input('url'),
        'published_on'  => $request->input('publishedOn'),
    ];

    $Article = ArticleModel::create($ArticleData)->toArray();

    return view('article.create.complete', [
        'article' => $Article
    ]);
}
```

### FormRequest
```php:StoreArticlePost.php

public function rules()
{
    return [
        'title'        => 'required|string|max:64',
        'body'         => 'required|string',
        'url'          => 'nullable|active_url',
        'publishedOn'  => 'required|date_format:Y-m-d',
    ];
}

public function attributes()
{
    return [
        'title'        => 'タイトル',
        'body'         => '本文',
        'url'          => 'URL',
        'publishedOn'  => '公開日',
    ];
}
```

### Route
```php:www.php
Route::post('/article', 'ArticleController@storeArticle')->name('Article/Store');
```

## テストコード
同じく必要なとこ(クラス宣言とか省いてる)しか書いてないです。

```php:tests/Feature/ArticleController/StoreTest.php
use ArticleModel;

/**
 * Article/Store
 * 正常系
 *
 * @dataProvider storingArticleData
 * @return void
 */
public function testStoreArticle($params)
{
    $url = route('Article/Store');
    $response = $this->post($url, $params['requestData']);
    $response->assertStatus(200);

    $this->assertDatabaseHas('articles', [
        'title'        => $params['requestData']['title'],
        'body'         => $params['requestData']['body'],
        'url'          => $params['requestData']['url'],
        'published_on' => $params['requestData']['publishedOn'],
    ]);
    $response->assertViewIs('article.create.complete');

    // dataProviderで指定したtitleがユニークである前提
    $article = ArticleModel::where('title', $params['requestData']['title'])->first()->toArray();
    $response->assertViewHas('article', $article);
}

/**
 * Article/Store
 * 異常系: バリデーションに引っかかる
 *
 * @dataProvider validationErrorData
 * @return void
 */
public function testStoreArticleValidationError($params)
{
    $url = route('Article/Store');
    $response = $this->post($url, $params['requestData']);
    $response->assertStatus(302);

    $this->assertDatabaseMissing('articles', [
        'title'        => $params['requestData']['title'],
        'body'         => $params['requestData']['body'],
        'url'          => $params['requestData']['url'],
        'published_on' => $params['requestData']['publishedOn'],
    ]);
}

/*
|--------------------------------------------------------------------------
|  DataProvider
|--------------------------------------------------------------------------
*/

public function storingArticleData()
{
    return [
        'valid data' => [
            [
                'requestData' => [
                    // タイトルはユニークである前提
                    'title'        => 'New Title',
                    'body'         => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
                    // アクティブなURL(今回の場合は自分のブログのURLを使用)
                    'url'          => 'http://nus3.moo.jp/',
                    'published_on' => '2019-01-14',
                ],
            ]
        ]
    ];
}

public function validationErrorData()
{
    return [
        // HACK: null用のデータセットが冗長のような気もする
        'validation: title is null' => [
            [
                'requestData' => [
                    'title'        => null,
                    'body'         => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
                    'url'          => 'http://nus3.moo.jp/',
                    'published_on' => '2019-01-14',
                ],
            ]
        ],
        'validation: body is null' => [
            [
                'requestData' => [
                    'title'        => 'New Title',
                    'body'         => null,
                    'url'          => 'http://nus3.moo.jp/',
                    'published_on' => '2019-01-14',
                ],
            ]
        ],
        'validation: url is invalid' => [
            [
                'requestData' => [
                    'title'        => 'New Title',
                    'body'         => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
                    'url'          => 'https://aaaaaaaaaaaaaa/',
                    'published_on' => '2019-01-14',
                ],
            ]
        ],
        // TODO: 他の項目がnullの場合のデータセット作る
    ];
}
```

## 注意事項
日々うんたらこんたら悩みながら書いてるコードです。
正しいとは限らないのでご注意ください。
hadaの学びにもなるので、何かありましたらコメントや編集リクエストいただけるとありがたいです_:(´ཀ`」 ∠):