Laravel3、コントローラークラスのテスト
この記事は、Laravel3でのテストについてのシリーズです。
- その1:単体テストの準備
- その2:例外のテスト
- その3:Eloquentモデルのテスト
- その4:バリデータークラスのテスト
- その5:リポジトリークラスのテスト
- その6:コントローラークラスのテスト
- その7:結合テスト
テストの対象は、以下のシリーズで作成した、りんごテーブルに対する簡単なCRUD操作プログラムに対するものです。
- 参照:Laravel3で活かす例外
- 参照:クラスのディレクトリー構造をひと工夫
- 参照:例外と小さなクラス達
- 参照:小さなクラスで読み込み削除
- 参照:小さなクラスで更新
コントローラーの単体テスト
りんごテーブルのCRUDシリーズでは、クラス分割と例外を使用し、コードを単純で簡単にしました。その恩恵を一番受けているのがコントローラーのクラスです。
りんごコントローラーをもう一度見てみましょう。
<?php use ProjectH\Exceptions\ExecutionErrorExceptions\IdNotFoundException; use ProjectH\Exceptions\ExecutionErrorExceptions\ValidationFaildException; class Apple_Controller extends Base_Controller { public $restful = true; private $appleRepo; public function __construct() { $this->appleRepo = IoC::resolve( 'AppleRepo' ); } function get_create() { return view( 'apple.create' ); } function post_create() { try { $this->appleRepo->create( Input::only( array ( 'name', 'color' ) ) ); } catch ( ValidationFaildException $e ) { return Redirect::back() ->with_input() ->with_errors( $e->validator ); } return Redirect::home(); } function get_show( $id ) { try { $apple = $this->appleRepo->read( $id ); } catch ( IdNotFoundException $e ) { return Redirect::error( '404' ); } return view( 'apple.read' ) ->with( 'apple', $apple ); } function get_delete( $id ) { try { $this->appleRepo->delete( $id ); } catch ( IdNotFoundException $e ) { return Redirect::error( '404' ); } return Redirect::home(); } function get_update( $id ) { $inputs = Input::only( array ( 'name', 'color' ) ); if ( empty( $inputs ) ) { try { $apple = $this->appleRepo->read( $id ); } catch ( IdNotFoundException $e ) { return Redirect::error( '404' ); } Input::replace( $apple->to_array() ); } return view( 'apple.create' ); } function post_update( $id ) { try { $this->appleRepo->update( $id, Input::only( array ( 'name', 'color' ) ) ); } catch ( IdNotFoundException $e ) { return Redirect::error( '404' ); } catch ( ValidationFaildException $e ) { return Redirect::back() ->with_input() ->with_errors( $e->validator ); } return Redirect::back(); } }
とても単純なので、単体テストも単純になります。同じようなコードをいくつも書いても仕方ないので、今回はコンストラクタのテストと生成のテストのみです。
テストコード
<?php class AppleControllerTest extends \PHPUnit_Framework_TestCase { public function testSetAppleRepoPropertyInConstructor() { IoC::register( 'AppleRepo', 'TestStubs\\AppleController\\AppleRepoStub' ); $appleController = Controller::resolve( 'application', 'apple' ); $ref = new ReflectionClass( $appleController ); $prop = $ref->getProperty( 'appleRepo' ); $prop->setAccessible( true ); $appleRepo = IoC::resolve( 'AppleRepo' ); // このテストで取得したAppleRepoクラスのインスタンスと、 // コンストラクタで取得されるAppleRepoクラスのインスタンスの比較 $this->assertEquals( $appleRepo, $prop->getValue( $appleController ) ); } public function testGetCreate() { IoC::register( 'AppleRepo', 'TestStubs\\AppleController\\AppleRepoStub' ); $appleController = Controller::resolve( 'application', 'apple' ); $view = $appleController->get_create(); $this->assertEquals( 'Laravel\\View', get_class( $view ) ); $this->assertEquals( View::make( 'apple.create' ), $view ); } public function testPostCreateNormalTerminal() { IoC::register( 'AppleRepo', 'TestStubs\\AppleController\\AppleRepoStub' ); $appleController = Controller::resolve( 'application', 'apple' ); $redirect = $appleController->post_create( array ( ) ); $this->assertEquals( 'Laravel\\Redirect', get_class( $redirect ) ); $this->assertTrue( $redirect->foundation->isRedirection() ); } public function testPostCreateValidationFaild() { IoC::register( 'AppleRepo', 'TestStubs\\AppleController\\AppleRepoValidationFaildStub' ); $appleController = Controller::resolve( 'application', 'apple' ); $redirect = $appleController->post_create( array ( ) ); $this->assertEquals( 'Laravel\\Redirect', get_class( $redirect ) ); $this->assertTrue( $redirect->foundation->isRedirection() ); } // 他のメソッドは省略 }
コントローラーのコードは、ほとんどがある例外が起きたらこのクラス、こちらの例外が起きたら別のクラスをリターンしているだけです。ですから単体テストとしては、リターンされたクラスのインスタンスを確認しています。
もっと努力すれば、インスタンスの内容を細かくチェックできます。しかし、コントローラーは単純ですから、正直ここで単体テストのレベルで頑張っても、意味深いテストはできませんね。
ちなみにget_updateメソッドでは、Inputクラスメソッドの返す内容による分岐があります。Input::replaceメソッドで入力を指定してあげる事ができます。
もしくは、フォームの内容をシュミレートするため、次の方法も取ることができます。
Request::foundation()->request->add( array ( 'name' => '赤りんご', 'color' => '白' ) );
詳しくは次の結合テストの記事で紹介します。
スタブ
まずは、正常に終了させるために例外を投げないスタブです。
<?php namespace TestStubs\AppleController; class AppleRepoStub { function create( $data ) { } }
単純ですね。
次は逆に例外を発生させ、catchのルートを確認するためのスタブです。
<?php namespace TestStubs\AppleController; use ProjectH\Exceptions\ExecutionErrorExceptions\ValidationFaildException; class AppleRepoValidationFaildStub { function create( $data ) { $val = \Validator::make( array ( ), array ( ) ); throw new ValidationFaildException( $val ); } }
スタブですから、手をかけず単純にしておきましょう。
正直、個人で開発しており、単体テストがどうしても必要(納品しなくてはならない)のでない限り、次に紹介する結合テストがきっちり行なっていればOKだと私は思います。
ああ、さらっと書いてしまっていい忘れましたが、このようにLaravelでは3の現在でもコントローラーのテストはやりやすくなっています。4になれば、さらに便利になります。