LaravelでPHPUnit入門

本記事は、[Laravelで認証用APIのテストを書く](https://qiita.com/curosuke/items/1ab4658e0c8a965de796)の記事を、APIではなくWeb画面用に書き換えたものです。
また、PHP Unitを実行しやすくするために、Laravel環境を利用＋認証画面のセットアップも手順に含んでいます。

##  テストコードを書く理由
1. テストのコストを下げるため  
※ ユニットテストはコードを網羅的に動かすため、一度書いてしえばリグレッションテストのコストを下げられる
2. テストをしやすいコードにすることで疎結合なプログラムを意識してかけるようになるため  
※ つまり **「メンテナンスしやすいコード」** が書けるようになる
3. テストコード自体がプログラマーに向けての仕様書になるから
※ 設計書(ドキュメント)として仕様を管理するのか、テストコードとして仕様を管理するかの違い  
※ ドキュメントを書かないなら、テストケースは書きたい
4. テストコードを考えることによって、要件への理解が深まるから
5. プログラムの変更/追加に伴うデグレを検知しやすくなるから

※ **[こちら](https://speakerdeck.com/twada/strategy-and-tactics-of-building-automated-testing-culture-into-organization-2019-summer-edition)にアップされている資料が神がかり的に分かりやすいので、一読を推奨**

## 事前準備
### ライブラリ依存管理ツールの用意
- コマンドラインからcomposerとnpmコマンドが実行できるようにしておく
   - [Composer](https://getcomposer.org/download/)
   - [Node.js](https://nodejs.org/ja/download/)

### Laravelプロジェクトを作成
```bash
composer create-project "laravel/laravel=6.*" test-sample
cd test-sample
```

### 必要なnpmライブラリのインストール＋ビルド
```bash
npm install
npm run dev
```

### テストで利用する認証系のルーティング＋ビューを用意
```
composer require laravel/ui --dev
php artisan ui vue --auth
```
ref: https://readouble.com/laravel/6.x/ja/authentication.html

### Laravelの機能を使い、ビルドインウェブサーバを立ち上げる
```
cd test-sample
php artisan serve
```

### 動作確認
- http://127.0.0.1:8000/login にアクセスして、以下のような画面が出ていればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165108/400f88ce-5ec6-d553-8581-fe28ff3190cd.png)

※ DBのセットアップをしていないので、画面からユーザ登録やログインはできない（設定すればできるので、興味ある人は調べてみてください）


## テスト作成
Laravelインストール時に最初から用意されている「UserFactory」を利用し、上記で作成したユーザ認証機能のテストを作成する

### テストケースの作成
```bash
php artisan make:test AuthenticationTest
```

### テストケースの修正
```php:tests/Feature/AuthenticationTest.php
   <?php

   namespace Tests\Feature;

   use App\User;

   use Tests\TestCase;
   use Illuminate\Foundation\Testing\WithFaker;
   use Illuminate\Foundation\Testing\RefreshDatabase;

   class AuthenticationTest extends TestCase
   {
       use RefreshDatabase;

       protected $user;
       protected $password = 'i-love-laravel';

       public function setUp(): void
       {
           parent::setUp();

           // テストユーザ作成
           $this->user = factory(User::class)->create([
               'password' => bcrypt($this->password)
           ]);
       }

       /**
        * ログイン認証テスト
        */
       public function testLogin(): void
       {
           // 作成したテストユーザのemailとpasswordで認証リクエスト
           $response = $this->post(route('login'), [
               'email' => $this->user->email,
               'password' => $this->password,
           ]);

           // リクエスト送信後、正しいくリダイレクト処理されていることを確認
           $response->assertRedirect('/home');

           // 指定したユーザーが認証されていることを確認
           $this->assertAuthenticatedAs($this->user);
       }

       /**
        * ログアウトテスト
        */
       public function testLogout(): void
       {
           // actingAsヘルパで現在認証済みのユーザーを指定する
           $response = $this->actingAs($this->user);

           // ログアウトページへリクエストを送信
           $response->post(route('logout'));

           // ユーザーが認証されていないことを確認
           $this->assertGuest();
       }
   }
```

## テストの実行
1. プロジェクトルートで、以下コマンドを実行
   ```bash
   ./vendor/bin/phpunit
   ```
2. 以下の様に結果が表示されていればOK
![phpunit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165108/d376162f-e936-8d8b-dfff-01b33ad7b725.png)

## 解説
- Laravelインストール時にComposerが最初に用意してくれているPHP Unitを利用
- `phpunit.xml`の初期設定で、インメモリのSQLiteを利用する設定を利用している（そのため、DBをセットアップしていなくてもfactoryでデータの作成＋テストが可能になっている）
- `assertAuthenticatedAs `や`assertGuest `など、Laravel側が用意してくれているアサーションを利用することで、セッション情報などをチェックせずとも手軽にテストが可能となっている  

## 参考サイト
- [組織にテストを書く文化を根付かせる戦略と戦術（2019夏版） / Strategy and Tactics of Building Automated Testing Culture into Organization 2019 Summer Edition - Speaker Deck](https://speakerdeck.com/twada/strategy-and-tactics-of-building-automated-testing-culture-into-organization-2019-summer-edition)
- [Testing Laravel authentication flow - Dariusz Czajkowski - Medium](https://medium.com/@DCzajkowski/testing-laravel-authentication-flow-573ea0a96318)
- [HTTPテスト 6.x Laravel](https://readouble.com/laravel/6.x/ja/http-tests.html)