ファサードをモックする


testing phpunit mockery

 悩み事

ユニットテストでファサードをモックしたい

 解決方法

Laravelは、各コンポーネントを簡単に利用できる様に
スタティックなインターフェースが提供されています

App, HTML, DBなど多くのコンポーネントがファサード(Facade)を介して
利用できる様になっています

LaravelのFacadeは、デザインパターンのプロキシパターンを利用しています

デザインパターン
Proxy パターン

Laravelのコアなユーザーの方たちは、
実際に上記のApp, HTML, DBなどはクラスとして存在していない事は
ご存知だと思います

これらは全てIoCコンテナで管理され、
各ファサードのメソッドが実行されると、
IoCコンテナから該当するインスタンスを取得して実行される様になっています

こういったLaravelのファサードを利用したクラス等をテストする際に、
モックしなければならないケースが発生しますが、
Laravelのこれらのファサードを簡単にモックできる様にMockeryが採用されています

Mockeryインストール

Mockeryを利用する場合は、composer.jsonで指定します

    "require-dev": {
        "phpunit/phpunit": "4.*",
        "mockery/mockery": "0.*"
    },

本番環境等で不要な場合は --no-dev を指定してください

ファサード モック

DB::connection()をモックする場合は、以下の様になります

\DB::shouldRecive('connection')

上記の様にshouldRecive()を利用します
これを利用するとMockeryのモックオブジェクトが取得でき、
それ以降はメソッドチェインでMockeryのメソッドを利用します

shouldRecive()を利用する場合は、必ずIlluminate\Foundation\Testing\TestCaseまたは
デフォルトで提供されているapp/tests/TestCase.phpを継承して
setUp()を実行するようにしてください

またMockeryはスタンドアロンのモックオブジェクトフレームワークとして使用できる様なデザインとなっているので、
phpunitらと統合するには、tearDown()メソッドを定義します

use Mockery as m;

class AbstractRepositoryTest extends TestCase
{

    public function tearDown()
    {
        m::close();
    }

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

}

これでユニットテストを実行する準備が整いました

先ほどのDB::connection()をサンプルにすると以下の様になります

// \DB::connection('slave')->table('hoge')->get() をモックします
$builder = m::mock("\Illuminate\Database\Query\Builder");
$builder->shouldReceive('table')->once('hoge')->andReturn($builder);
$builder->shouldReceive('get')->once()->andReturn(m::mock("stdClass"));
\DB::shouldReceive('connection')->with('slave')->once()->andReturn($builder);

 アドバイス

Mockeryについては、レシピ以上の内容となってしまいますので、
公式リファレンスやpadraic/mockery
日本語で記述されているMockery 0.8.0 日本語ドキュメント
をご覧ください

お好みでphpunitのモックを利用しても構いません
モックオブジェクト


Author:Yuuki Takezawa