March 22, 2014

オレオレPhalconで依存注入しつつテストしてみる

前回の記事で作ったAjaxFilter#filter()をテストしてみます。
http://madroom-project.blogspot.jp/2014/03/phalcon_1056.html

ディレクトリ構造とかは、適当に作っているオレオレPhalconを御覧ください。
http://madroom-project.blogspot.jp/2014/03/phalcon_21.html
https://github.com/mamor/phalcon-myapp

以下にPhalcon用の便利ツールっぽいものもあるのですが、今回は使わずにやってみます。
https://github.com/phalcon/incubator

まず app/bootstrap/helper.php を作成して、ヘルパ関数を作ってみました。di()は、Phalcon\DI\FactoryDefaultのインスタンスを返します。
<?php

use Phalcon\DI;

/**
 * @return Phalcon\DI\FactoryDefault
 */
function di()
{
    static $di = null;

    if (is_null($di)) {
        $di = (new DI)->getDefault();
    }

    return $di;
}
このファイルは app/bootstrap/bootstrap.php で読み込みます。
require_once __DIR__.'/helper.php';

次に composer.jsonに "phpunit/phpunit" と "mockery/mockery" と、この後作成する "app/tests/TestCase.php" を追加して composer update します。Mockeryは無くても良いですが、PHPUnitよりも少し簡単にモックが書けます。
 {
+    "require-dev": {
+        "phpunit/phpunit": "4.*",
+        "mockery/mockery": "0.*"
+    },
     "autoload": {
+        "classmap": [
+            "app/tests/TestCase.php"
+        ],
         "psr-0": {
             "Core": "app/classes",
             "App": "app/classes"

app/tests/TestCase.php を作成します。この後に作成するテストクラスの基底クラスになります。setMockToDI()は依存注入用メソッドです。
<?php

/**
 * Class TestCase
 */
abstract class TestCase extends \PHPUnit_Framework_TestCase {

    /**
     * @param  string $name
     * @param  mixed $mock
     * @param  bool $shared
     */
    protected function setMockToDI($name, $mock, $shared = null)
    {
        di()->set($name, function () use ($mock) {
            return $mock;
        }, $shared);
    }
}

プロジェクトルートに phpunit.xml を作成します。 http://docs.phalconphp.com/en/1.2.6/reference/unit-testing.html を基にしました。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./app/tests/bootstrap.php"
         backupGlobals="false"
         backupStaticAttributes="false"
         verbose="true"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="true">
    <testsuite name="Phalcon - Testsuite">
        <directory>./app/tests/</directory>
    </testsuite>
</phpunit>

app/tests/bootstrap.php を作成します。composer の autoload.php と、前述のヘルパファイルを読み込んでいます。
<?php

include __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__.'/../bootstrap/helper.php';

app/tests/ の下に、名前空間とかを加味して、テストクラスを作成します。今回は app/tests/Core/Filter/AjaxFilterTest.php としています。内容の細かいことは最後に書きます。
<?php namespace Core\Filter;

use Mockery as m;

/**
 * Class AjaxFilterTest
 */
class AjaxFilterTest extends \TestCase {

    /**
     * test for filter()
     */
    public function testFilter() {
        $m = m::mock();
        $m->shouldReceive('getServer')->once()->with('X_REQUESTED_WITH')->andReturn('xmlhttprequest');
        $this->setMockToDI('request', $m);

        $this->assertTrue((new AjaxFilter)->filter());
    }

    /**
     * test for filter()
     */
    public function testFilterFailed() {
        $m = m::mock();
        $m->shouldReceive('getServer')->once()->with('X_REQUESTED_WITH')->andReturn(null);
        $this->setMockToDI('request', $m);

        $this->assertFalse((new AjaxFilter)->filter());
    }
}

phpunitを実行してみます。
$ phpunit
PHPUnit 4.0.12 by Sebastian Bergmann.

Configuration read from /share/phalcon_project/phpunit.xml

..

Time: 302 ms, Memory: 6.00Mb

OK (2 tests, 2 assertions)

AjaxFilterTestクラスでは、Mockeryのモックオブジェクトを作成しています。このモックオブジェクトを、基底テストクラスのsetMockToDI()メソッドに渡して依存注入して、AjaxFilter#filter()におけるgetServer()をコントロールしている感じです。

もっと良い方法あれば教えて下さいm(_ _)m (Phalcon\DI\Injectableとか上手く使えないかな。。。)


No comments:

Post a Comment