Laravel5.3のものをこちらに書きました → Laravel5.3でのテストのメモ - tanaka's Programming Memo
-
- -
自分向けのメモです。
LaravelにはPHPUnitによるテストの設定が組み込まれています。公式マニュアルで概要を確認して、データベースのチェックや、URLごとのテストについて調べました。
現時点で、Laravelは5.3がリリースされていますが、Sentinelがまだ対応していないので、5.2の方を調べました。
目次
実行時の注意点
以下のようなエラーが表示された場合の対処方法です。
PHP Fatal error: Call to undefined method PHPUnit_Framework_TestResult::warnings() in /Users/yutanaka/.composer/vendor/phpunit/phpunit/src/TextUI/ResultPrinter.php on line 297
Laravelに組み込まれているphpunitはやや古い可能性があります。グローバルのphpunitとバージョンが異なる場合に、上記のようなエラーが表示される場合があります。その際は、Laravelに組み込まれているphpunitを呼び出すように package.json に以下のようなスクリプト呼び出しを追加して、 npm test でテストを開始すると良いでしょう。
"scripts": { // : "test": "vendor/phpunit/phpunit/phpunit" },
公式マニュアル概要
Testing - Laravel - The PHP Framework For Web Artisans を参照して、概要を確認します。
Introduction
テスト環境
アプリケーションのテスト
- Laravelは、HTTPリクエストを生成したり、出力をテストしたり、フォームを埋めたりするのが楽になるAPIを提供する
- visit()メソッドは、GETリクエストをアプリケーションに発行する
- see()メソッドは、アプリケーションの戻り値から指定した文字列が含まれるかをアサートする
- dontSee()メソッドは、指定の文字列が戻り値に含まれないことをアサートする
アプリケーションとの連携
- リンクをクリックする
- visit()で画面を表示
- click('<クリックしたい文字列>')
- seePageIs('遷移先のURL')
- フォームの操作
JSONテスト
- get, post, put, patch, deleteメソッドで、指定のURLに、指定のパラメータを渡した呼び出しができる
- seeJson()で、指定のデータが戻り値に含まれるかをチェックする。「含むか」なので、完全一致じゃなくてもテストは成功する
- JSONの完全な一致をチェックしたい場合は、seeJsonEquals()メソッドを利用する
- 戻り値の内容ではなく、構造をチェックしたい場合は、seeJsonStructure()メソッドを使う。指定していないキーがあっても、指定した構造が含まれていればテストは成功する
- 何らかのキーに、指定の構造が含まれるかをチェックする場合は、 * を使う。ネストも可能
SessionとAuthentication
- withSession()メソッドで、セッションを設定できる。ページを訪れる前に、テストしたい値をセッションに設定しておくことができる
- actingAs()で、カレントユーザーを設定できる。事前に、ModelFactoryで、新規のユーザーモデルを作成して、それを引数に渡してユーザーを作成できる
- actingAs()の第2引数を指定すると、毎回認証が必要な保護認証に対して、保護名を設定できる
ミドルウェアの無効化
カスタムHTTPリクエスト
- カスタムリクエストを作成して、戻り値のIlluminate\Http\Responseオブジェクトを取得したい場合は、call()メソッドを使う
- POST, PUT, PATCHリクエストに必要な入力データは、配列で渡す
PHPUnitアサーション
以下、Laravelが提供するPHPUnitテスト用のメソッド。
- assertResponseOk();
- クライアントのレスポンスコードがOkかを判定
- assertResponseStatus($code);
- ステータスコードを判定
- assertViewHas($key, $value = null);
- 戻り値のビューに対して、$keyに$valueが設定されているかを判定
- assertViewHasAll(array);
- 戻り値が、指定の配列を持つかを判定
- assertViewMissing($key);
- 戻り値のビューに指定のkeyが含まれないことを判定
- assertRedirectedTo($uri, $with = []);
- 指定のURIにリダイレクトしたかを判定
- assertRedirectedToRoute($name, $parameters = [], $with = []);
- 指定のルートにリダイレクトされたかを判定
- assertRedirectedToAction($name, $parameters = [], $with = []);
- 指定のアクションにリダイレクトされたかを判定
- assertSessionHas($key, $value = null);
- セッションが指定のkeyとvalueを持つかを判定
- assertSessionHasAll(array $bindings);
- セッションが指定の配列の値を持つかを判定
- assertSessionHasErrors($bindings = [], $format = null);
- セッションが指定のエラーになっていないかを判定
- assertHasOldInput();
- セッションがold inputを持っていないかを判定
- ssertSessionMissing($key);
- セッションが指定のkeyを持っていないことを判定
Databaseのテスト
Laravelは、データベース駆動のアプリケーション向けのテスト環境も提供しています。
- seeInDatabase()メソッドで、指定のデータが、データベースの指定のテーブルに含まれるかを判定
テスト後のデータベースのリセット
各テスト後に、テスト時のデータベースへの変更をもとに戻す方法です。
モデルファクトリー
複数のテストにまたがって、共通の幾つかのレコードをデータベースに登録したい場合、手動で特定の値を列ごとに設定するのではなく、Eloquentのモデルファクトリーを利用することができます。
- database/factories/ModelFactory.php ファイルに、データの定義例がある
- $factory->define()メソッドに、デフォルトデータを戻すクロージャーを渡す
- クロージャーは、Faker PHP ライブラリのインスタンスを返し、テストのための乱数を設定できる
- ModelFactory.phpファイルには、自由にファクトリーを追加できる
- UserFactory.phpやCommentFactory.phpなど、database/factoriesディレクトリーに加えることができる
同じEloquentモデルクラスに対して、複数のファクトリーを利用するには、ベースとなるユーザーファクトリーを複製して、raw()メソッドでベースファクトリーを変更する方法があります。$factory->defineAs()メソッドでファクトリーを定義して、クロージャー内で$factoryを受け取って、$factory->row()でモデルクラスを受け取って、array_merge()で配列を結合します。
- ファクトリーのテストでの使い方
- ファクトリーモデルを持続させる
- リレーションの追加
- create()してモデルを作成後、each()メソッドを呼び出して、posts()->save(factory(モデル暮らす)->make()); で設定できる
- ファクトリーを定義するファクトリーに渡すクロージャーでモデルのリレーションを設定することもできる
- Postを作成する時に、新しいユーザーを作成して、idをリレーションさせることができる
- 生成したユーザーのIDを、その後の属性で利用することも可能
モック
イベントのモック
- Laravelで大量にイベントシステムを構築していた場合、テスト中にイベントをモック化できる
- ユーザー登録をした時に、登録が完了したイベント(例えばUserRegistered)が発行すると、登録完了のメールが送信されてりする。それをキャンセルするとテストが楽である
- $this->expectsEvents(App\Events\UserRegistered::class); とすると、UserRegisteredイベントをキャンセルできる
- doesntExpectEvents()メソッドを使うと、指定のイベントが発動しなかったことを確認できる
- withoutEvents()メソッドを呼び出すと、すべてのイベントハンドラを抑制できる
ジョブをモック
アプリケーションがリクエストを作成した時に、作成したコントローラーが特定のジョブを呼び出すかを試す簡単なテストをしたい場合があるでしょう。そのような時の方法です。これにより、ルートとコントローラーのテストを分離することができます。
- $this->expectsJobs(App\Jobs\PurchasePodcast::class);などとすることで、指定のジョブが呼び出されたことを確認する。行うのは確認のみで、ジョブ自体を実行することはしない
- このメソッドは、DispatchesJobsトレイトか、dispatch()メソッドによって発行されたジョブにのみ反応する。Queue::push()で直に送信されたジョブは対象外