読者です 読者をやめる 読者になる 読者になる

Ethnaを業務で使うために(3) テスト関連のディレクトリ構造の変更

Ethnaを業務で使うために(2) ディレクトリ構造の変更 - maru.cc@はてな」の続きです。
前回は、AppManagerと、AppObjectのディレクトリ変更を行いましたが、次にテスト関連のファイルのディレクトリ変更を行います。


UnitTestしてますか?
ある程度の規模になり、開発者が複数になったり、運用を続けて担当者が入れ替わる場合、自動テストが無いと、怖くて触れなくなってしまいます。


Ethna2.3.2にも SimpleTestを使用した UnitTestの機構が準備されていますが、ちょっと微妙だと思う場所があります。
それは、実行ファイルと同じ場所にテストファイルが作成されることです。


例えば

$ sh ethna.sh add-action test/index
file generated [/path/to/common/skel/skel.action.php -> /path/to/common/app/action/Test/index.php]
action script(s) successfully created [/path/to/common/app/action/Test/index.php]
[maruta@php52x common]$ sh ethna.sh add-action-test test/index
file generated [/path/to/common/skel/skel.action_test.php -> /path/to/common/app/action/Test/indexTest.php]
action test(s) successfully created [/path/to/common/app/action/Test/indexTest.php]
$ ls app/action/Test/
index.php  indexTest.php

※このファイルは確認なので消しておいてください。


これだけならば、作業するファイルと近い位置に置くという意味では、問題ないとは思いますが、Ethnaの機能として、Actionファイルが指定した命名規則で見つからなかった場合に、app/Action/以下のファイルを全て読み込むという動作をするのです。


それによって発生する問題として次のようなものがあります。

  • 本番に不要なテストファイルが上がってしまう(除外するにしてもファイル名単位なので煩雑になる)
  • 本番で不要なファイルが実行時にincludeされてしまう
  • 本番にSimpleTestが入っていないと動かなくなってしまう

などなど。


なので、ディレクトリ単位で除外できるように、テスト関連のファイルは全て一箇所にまとめてしまいます。
プロジェクト直下に testディレクトリを作成し、それ以下に置くようにします。

test
├action/
├action_cli/
├action_xmlrpc/
├manager/
├view/
├info.php
└unittest.php

info.php、unittest.php については、1回目のディレクトリ変更で移動したものです。

Generator_ActionTestの拡張

app/plugin/Gnerator/ 以下にファイルを作成します。
Common_Plugin_Generator_ActionTest.php

<?php
// vim: foldmethod=marker
/**
 *  Common_Plugin_Generator_ActionTest.php
 *
 *  @author
 *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
 *  @package    Ethna
 *  @version    $Id:  $
 */

// {{{ Common_Plugin_Generator_ActionTest
/**
 *  スケルトン生成クラス
 *
 *  @author
 *  @access     public
 *  @package    Ethna
 */
class Common_Plugin_Generator_ActionTest extends Ethna_Plugin_Generator
{
    /**
     *  アクション用テストのスケルトンを生成する
     *  (現在のところ GATEWAY_WWW のみ対応)
     *
     *  @access public
     *  @param  string  $action_name    アクション名
     *  @param  string  $skelton        スケルトンファイル名
     *  @param  int     $gateway        ゲートウェイ
     *  @return true|Ethna_Error        true:成功 Ethna_Error:失敗
     */
    function &generate($action_name, $skelton = null, $gateway = GATEWAY_WWW)
    {
        switch ($gateway) {
        case GATEWAY_WWW:
            $key = 'action';
            break;
        case GATEWAY_CLI:
            $key = 'action_cli';
            break;
        case GATEWAY_XMLRPC:
            $key = 'action_xmlrpc';
            break;
        }
        $action_dir = $this->ctl->getDirectory('test_'.$key).'/';
        $action_class = $this->ctl->getDefaultActionClass($action_name, $gateway);
        $action_form = $this->ctl->getDefaultFormClass($action_name, $gateway);
        $action_path = $this->ctl->getDefaultActionPath($action_name . 'Test');

        // entity
        $entity = $action_dir . $action_path;

        $app_dir = $this->ctl->getDirectory('test_action');
        Ethna_Util::mkdir(dirname($entity), 0755);

        // skelton
        if ($skelton === null) {
            $skelton = 'skel.action_test.php';
        }

        // macro
        $macro = array();
        $macro['project_id'] = $this->ctl->getAppId();
        $macro['action_name'] = $action_name;
        $macro['action_class'] = $action_class;
        $macro['action_form'] = $action_form;
        $macro['action_path'] = $action_path;

        // user macro
        $user_macro = $this->_getUserMacro();
        $macro = array_merge($macro, $user_macro);


        // generate
        if (file_exists($entity)) {
            printf("file [%s] already exists -> skip\n", $entity);
        } else if ($this->_generateFile($skelton, $entity, $macro) == false) {
            printf("[warning] file creation failed [%s]\n", $entity);
        } else {
            printf("action test(s) successfully created [%s]\n", $entity);
        }

        $true = true;
        return $true;
    }
}
// }}}
?>
Generator_ViewTestの拡張

同様に、ViewTest用のファイルを作成します。
Common_Plugin_Generator_ViewTest.php

<?php
// vim: foldmethod=marker
/**
 *  Common_Plugin_Generator_ViewTest.php
 *
 *  @author
 *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
 *  @package    Ethna
 *  @version    $Id:  $
 */

// {{{ Common_Plugin_Generator_ViewTest
/**
 *  スケルトン生成クラス
 *
 *  @author
 *  @access     public
 *  @package    Ethna
 */
class Common_Plugin_Generator_ViewTest extends Ethna_Plugin_Generator
{
    /**
     *  ビュー用テストのスケルトンを生成する
     *
     *  @access public
     *  @param  string  $forward_name   ビュー名
     *  @param  string  $skelton        スケルトンファイル名
     *  @return true|Ethna_Error        true:成功 Ethna_Error:失敗
     */
    function &generate($forward_name, $skelton = null, $gateway = GATEWAY_WWW)
    {
        $view_dir = $this->ctl->getDirectory('test_view').'/';
        $view_class = $this->ctl->getDefaultViewClass($forward_name, $gateway);
        $view_path = $this->ctl->getDefaultViewPath($forward_name . 'Test');

        // entity
        $entity = $view_dir . $view_path;
        Ethna_Util::mkdir(dirname($entity), 0755);

        // skelton
        if ($skelton === null) {
            $skelton = 'skel.view_test.php';
        }

        // macro
        $macro = array();
        $macro['project_id'] = $this->ctl->getAppId();
        $macro['forward_name'] = $forward_name;
        $macro['view_class'] = $view_class;
        $macro['view_path'] = $view_path;

        // user macro
        $user_macro = $this->_getUserMacro();
        $macro = array_merge($macro, $user_macro);


        // generate
        if (file_exists($entity)) {
            printf("file [%s] already exists -> skip\n", $entity);
        } else if ($this->_generateFile($skelton, $entity, $macro) == false) {
            printf("[warning] file creation failed [%s]\n", $entity);
        } else {
            printf("view test(s) successfully created [%s]\n", $entity);
        }

        $true = true;
        return $true;
    }
}
// }}}
?>
Common_Controllerの修正

app/Common_Controller.phpに、必要なディレクトリ指定のパスを追記します。


アプリケーションディレクトリに追記を行います。
app/Common_Controller.php 90行目あたり
修正前

    /**
     *  @var    array       アプリケーションディレクトリ
     */
    var $directory = array(
        'action'        => 'app/action',
        'action_cli'    => 'app/action_cli',
    (略)
        'tmp'           => 'tmp',
        'view'          => 'app/view',
        'www'           => 'www',
        'manager'       => 'app/manager',
        'object'        => 'app/manager/object',
    );

修正後

    /**
     *  @var    array       アプリケーションディレクトリ
     */
    var $directory = array(
        'action'        => 'app/action',
        'action_cli'    => 'app/action_cli',
    (略)
        'tmp'           => 'tmp',
        'view'          => 'app/view',
        'www'           => 'www',
        'manager'       => 'app/manager',
        'object'        => 'app/manager/object',
        'test'          => 'test',
        'test_action'   => 'test/action',
        'test_action_cli'       => 'test/action_cli',
        'test_action_xmlrpc'    => 'test/action_xmlrpc',
        'test_view'     => 'test/view',
        'test_manager'  => 'test/manager',
    );

※managerとobjectは、前回追加したものです。


動かしてみます

ここまで出来たら動かしてみます。

$ sh ethna.sh add-action-test test
file generated [/path/to/common/skel/skel.action_test.php -> /path/to/common/test/action/TestTest.php]
action test(s) successfully created [/path/to/common/test/action/TestTest.php]
$ sh ethna.sh add-view-test test
file generated [/path/to/common/skel/skel.view_test.php -> /path/to/common/test/view/TestTest.php]
view test(s) successfully created [/path/to/common/test/view/TestTest.php]

$ ls test/
action  info.php  unittest.php  view
$ ls test/action/
TestTest.php
$ ls test/view/
TestTest.php

※このファイルは確認なので消しておいてください。

次回

このままでは、実際のテストはまだ動きませんので、次回はテスト関連の整備を書きたいと思います。
Ethnaを業務で使うために(4) テストの整備 - maru.cc@はてな