Laravel5.1でユニットテストを行う

#はじめに
Laravelの開発が徐々に軌道に乗ってきたので、いよいよ本格的なテストに力を入れていこうと思います。
Laravelには様々なテスト機能がありますが、その中でも比較的理解できる部分に絞って書いていこうかと。
モックやらミドルウェアやら使えたら便利なんだろうけどなぁ...。

#初期設定
##データベース
テストを行うときに、データベースと連携してゴニョゴニョするような処理があると思います。
Laravelさんはかしこいので実環境のテストを行ったあとにロールバックしたり、テスト用のDBを用意したり、そもそもDBを用意する必要がなかったりといろいろ方法はあるみたいですが...。
今回はテスト用のDBを用意することにしました。
ただし、テストの性質からインメモリ機能を利用します。
###設定手順
設定を変更する必要があるファイルは以下の通りです。

+ config/database.php
+ phpunit.xml
+ tests/TestCase.php

```php:config/database.php
    'connections' => [
// 2017-10-05追記：不要
//        database_path('database.sqlite'),     // 変更
        'testing' => [
            'driver'   => 'sqlite',
            'database' => ':memory:',         // 追記
            'prefix'   => '',
            'options'  => [
                PDO::ATTR_PERSISTENT => true, // 追記
            ]
        ],
```

```xml:phpunit.xml
<php>
    <env name="APP_ENV" value="testing"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="QUEUE_DRIVER" value="sync"/>
    <env name="DB_DATABASE" value="testing"/> <!-- 追記 2017-10-05 DB_CONNECTION => DB_DATABASE へ -->
</php>
```

これでDBを実装することなくテストができるようになりました。

##View/Model
実際にテストを行う画面を以下に示しておきます。
###ログイン画面
```html:auth/login.blade.php
<div class="content panel panel-primary">
    <div class="panel-heading"><h5>Login</h5></div>
    <form class="form-horizontal panel-body" role="form" method="post" action="/auth/login">
        {!! csrf_field() !!}

        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="email">メールアドレス</label>
            <div class="col-md-7">
                <input type="text" class="form-control" name="email" value="{{ old('email') }}">
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="password">パスワード</label>
            <div class="col-md-7">
                <input type="password" class="form-control" name="password" value="{{ old('password') }}">
            </div>
        </div>

        <div class="col-md-offset-4 col-md-7">
            <button type="submit" class="btn btn-success btn-block btn-lg">ログイン</button>
        </div>
    </form>
</div>
```

###ユーザー登録画面
```html:auth/register.blade.php
<div class="content panel panel-primary">
    <div class="panel-heading"><h5>Register</h5></div>
    <form class="form-horizontal panel-body" role="form" method="post" action="/auth/register">
        {!! csrf_field() !!}
        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="name">ユーザー名</label>
            <div class="col-md-7">
                <input type="text" class="form-control" name="name" value="{{ old('name') }}">
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="email">メールアドレス</label>
            <div class="col-md-7">
                <input type="email" class="form-control" name="email" value="{{ old('email') }}">
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="password">パスワード</label>
            <div class="col-md-7">
                <input type="password" class="form-control" name="password" value="{{ old('password') }}">
            </div>
        </div>

        <div class="form-group">
            <label class="control-label col-md-3 col-md-offset-1" for="password_confirmation">パスワード確認</label>
            <div class="col-md-7">
                <input type="password" class="form-control" name="password_confirmation" value="{{ old('password_confirmation') }}">
            </div>
        </div>

        <div class="col-md-offset-4 col-md-7">
            <button type="submit" class="btn btn-warning btn-block btn-lg">登録</button>
        </div>
    </form>
</div>
```

###ルート
```php:app/Html/Routes.php
Route::get('auth/login',     'Auth\AuthController@getLogin');
Route::post('auth/login',    'Auth\AuthController@postLogin');
Route::get('auth/logout',    'Auth\AuthController@getLogout');
Route::get('auth/register',  'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
Route::get('home', function(){ return view('home'); });
```

この辺はマニュアルまんまですね。

#テスト
###テストコード生成
``php artisan make:test LoginTest``と打ち込んでテストコードを生成します。

###とりあえずテスト実行
``./vendor/bin/phpunit``コマンドを叩いて正常にテストができることを確認します。
導入に関しての詳しいお話はグーグル先生に丸投げしときます。
もし仮に、

```
1) Warning
No tests found in class "TestCase".
```

的なエラーコードが出たとしたら次のように対応してあげてください。

```php:tests/TesCase.php
// class TestCase extends Illuminate\Foundation\Testing\TestCase
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
{...
```

###テストコード生成
先ほど生成した``tests/LoginTest.php``をいじっていきます。

```php:tests/LoginTest.php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

use App\User;  // 状況に応じて使うモデルを定義
class sample extends TestCase
{

    use DatabaseMigrations; // これでテスト時にマイグレーションしてくれる

    /**
     * @before
     * テスト実行前に行う処理を記述できる。
     * 今回はデータベースのマイグレーションを実行した。
     */
    public function setUpDatabase()
    {
        $this->artisan('migrate');
    }
    public function testユーザー登録_成功(){
        $this->visit('/auth/register')
            ->type('test user','name')
            ->type('test@test.com','email')
            ->type('test123','password')
            ->type('test123','password_confirmation')
            ->press('登録')
            ->seePageIs('home')
        ;
        $this->seeInDatabase('users', [
            'name'=> 'test user',
            'email'=>'test@test.com',
        ]);
    }
    /**
     * @tests
     * testsアノテーションを付けることでテストとしてみなされる
     */
    public function ログイン成功()
    {
        $this->visit('/')
            ->see('ログイン')
            ->type('test@test.com','email')
            ->type('test123','password')
            ->press('ログイン')
            ->seePageIs('home')
        ;
    }
    public function testログイン後() {
        $user = User::first();
        $this->actingAs($user) // 注４
            ->visit('/chat')
            ->see("ようこそ、{$user->name}さん！")
        ;
    }
    ...
}
```

#所感
...という風に、アレをしてその後コレをして...って感じでテストを記述できるのですごくわかりやすいです。
ただこれ、あくまでもUI側のテスト的な側面が大きいんですよね。
実際はUI変更ありまくりだからController単体でテストしたかったり、もっと変更に耐えるテストコードにしたかったりとやりたいことは多いですが、頭が追いつかない。
おいおい勉強していくことにします。

ただひとつ言えることは、「画面・機能に合わせてテストコードを書く」のではなく、「テストしやすいように画面・機能を作っていく」ことが大事なのだということ。

あと個人的に疑問に思ってたこと「クラスの中のprivate関数に対してどうテストするのか？本来privateで記述する必要のあるものもpublicで書かなきゃいけないの？」ってのがあったんですが、「phpのリフレクションとかClosure::bindを利用すればいいんじゃないかな」って言われて目からうろこでした。

や、普通に考えればそんなアホみたいな話はないんですけどねー。