Laravel3、コントローラークラスのテスト

タグ: Laravel3   テスト志向  

この記事は、Laravel3でのテストについてのシリーズです。

テストの対象は、以下のシリーズで作成した、りんごテーブルに対する簡単なCRUD操作プログラムに対するものです。

コントローラーの単体テスト

りんごテーブルの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になれば、さらに便利になります。