LionHeart SD BLOG

株式会社ライオンハート システムデザインの技術ブログ

Phalcon PHPでPHPUnitを使ってテストコードを書く

こんにちは、株式会社ライオンハートの鵜飼です。

引き続きPhalcon PHPについて、今回はPhalcon上でのPHPUnitの書き方についてのメモです。

また、今回の記述はPhalcon Devtoolsでプロジェクトを作成している環境を想定しています。
app/config/config.phpや、app/config/services.phpがある環境)

Phalcon上のPHPUnitの環境作り

テストコードを格納するディレクトリは、appディレクトリと同階層にtestsという名前で作成しました。

/app
/public
/tests

このような形になると思います。

phalcon/incuvatorをインストール

ユニットテスト用の公式抽象クラスが配布されていますので、インストールしておきます。

composer require phalcom/incubator

phpunit.xmlを作成

公式ドキュメントを参考に、./tests/phpunit.xmlを作成していきます。

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./Bootstrap.php"
         backupGlobals="false"
         backupStaticAttributes="false"
         verbose="true"
         colors="false"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="true">
    <testsuite name="Phalcon - Testsuite">
        <directory>./</directory>
    </testsuite>
</phpunit>

ほぼそのままです、何となくのイメージで、bootstrapはBootstrap.phpにリネームしています。

ヘルパ関数を作成

コチラはあっているのかどうかは詳しくは不明ですが、通常稼働する環境になるべく近くなるように大分いじくっています。

<?php

use Phalcon\DI;
use Phalcon\Mvc\Router\Annotations as Router;

ini_set('display_errors',1);
error_reporting(E_ALL);

define('APP_PATH', realpath('..'));

// phalcon/incubator のために必要
include APP_PATH . "/vendor/autoload.php";

try {

    /**
     * Read the configuration
     */
    $config = include APP_PATH . "/app/config/config.php";

    /**
     * Read auto-loader
     */
    include APP_PATH . "/app/config/loader.php";

    // composerの依存関係をオートロードする
    $registerDirs = $loader->getDirs();
    $registerDirs[] = __DIR__;
    $loader->registerDirs( $registerDirs );
    $loader->register();

} catch (\Exception $e) {
    echo $e->getMessage();
}

なるべく設定ファイルは使い回すように、app/config/config.phpや、app/config/loader.phpを利用しています。

データベース周りも同じ設定になっちゃってるので、この辺りは要調整ですね…。

テストコードのベースクラスを作成

もう一度公式ドキュメントのコードを参考にさせていただき、ベースクラスを作成します。

大きな変更点は、

  • UnitTestCase -> FunctionalTestCaseに変更
  • $configをapp/config/config.phpから取得
  • $diをapp/config/services.phpで生成

という感じです。

<?php

use Phalcon\DI;
use Phalcon\Test\FunctionalTestCase as PhalconTestCase;

abstract class UnitTestCase extends PhalconTestCase
{
    /**
     * @var \Voice\Cache
     */
    protected $_cache;

    /**
     * @var \Phalcon\Config
     */
    protected $_config;

    /**
     * @var bool
     */
    private $_loaded = false;

    public function setUp(Phalcon\DiInterface $di = NULL, Phalcon\Config $config = NULL)
    {
        /**
         * Read services
         */
        $config = include APP_PATH . "/app/config/config.php";
        include APP_PATH . "/app/config/services.php";

        // ここで必要なDIコンポーネントを取得する。config があるなら、それを parent に渡すことを忘れずに
        parent::setUp($di, $config);

        $this->_loaded = true;
    }

    /**
     * Check if the test case is setup properly
     *
     * @throws \PHPUnit_Framework_IncompleteTestError;
     */
    public function __destruct()
    {
        if (!$this->_loaded) {
            throw new \PHPUnit_Framework_IncompleteTestError('Please run parent::setUp().');
        }
    }
}

これでPHPUnitを利用する環境が整いました。

テストコードの記述

試しにトップページ(app/controllers/indexController.php)にアクセスした場合のテストコードを書いてみます。

tests/IndexTest.phpという名前で下記のように記述しました。

<?php

class IndexTest extends \UnitTestCase
{
    public function testIndexAction()
    {
        $this->dispatch( '/' );
        $this->assertController( 'index' );
        $this->assertAction( 'index' );
        $this->assertResponseCode( 200 );
    }
}

実行してテスト出来ているか確認します。

$ phpunit
PHPUnit 4.8.5 by Sebastian Bergmann and contributors.

Runtime:    PHP 5.6.12
Configuration:  /path/to/tests/phpunit.xml

.

Time: 523 ms, Memory: 12.50Mb

OK (1 test, 3 assertions)

assertResponseCodeについてのメモ

今のところ原因は不明ですが、Controller側でsetStatusCodeで指定をしておかないと、assertResponseCodeで空文字が返ってきてしまいます。

$ phpunit
PHPUnit 4.8.5 by Sebastian Bergmann and contributors.

Runtime:    PHP 5.6.12
Configuration:  /path/to/tests/phpunit.xml

F

Time: 524 ms, Memory: 12.50Mb

There was 1 failure:

1) Test\IndexTest::testIndexAction
Failed asserting response code is "200", actual response status is ""

/path/to/vendor/phalcon/incubator/Library/Phalcon/Test/FunctionalTestCase.php:180
/path/to/tests/IndexTest.php:15

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

上の例で、エラーが出ないようにする時は、下記のように記述を追加しています。

<?php

class IndexController extends ControllerBase
{
    public function indexAction()
    {
        $this->response->setStatusCode( 200 );
    }
}

毎回書くのはしんどいので、エラーの時だけ書きたいのですが、全部書かないと行けないものなんですかね?

おまけ:PHPUnitのインストール

PHPUnitのインストールもかなり簡単になり、下記のコマンドを叩くだけで良くなりました。楽ちんです。

$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ sudo mv phpunit.phar /usr/local/bin/phpunit
$ phpunit --version
PHPUnit 4.8.5 by Sebastian Bergmann and contributors.