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になれば、さらに便利になります。