CakePHPシェルの単体テストのやり方

このエントリーをはてなブックマークに追加
はてなブックマーク - CakePHPシェルの単体テストのやり方
Share on Facebook

CakePHPのシェルの単体テストは少々面倒くさいです。WEB表示を前提としたコントローラやコンポーネントと違ってCLIへの出力をするので、CakePHP内のリクエスト順が違います。そのためDispatcherの設定などを変えなければなりません。

ただ、深く考えずにシェルのテストをしたいだけなら、Coreのテストからソースをコピーするだけで動きます。

以下に簡単なサンプルを載せます。

サンプル

テスト対象のシェル: uso.php

// app/vendors/shells/uso.php
<?php
class UsoShell extends Shell {

    function main() {
        $this->out("uhouho");
    }
}
?>

テストケース: uso.test.php

// app/tests/cases/shells/uso.test.php
<?php
App::import("Shell", "Uso");

if (!defined('DISABLE_AUTO_DISPATCH')) {
    define('DISABLE_AUTO_DISPATCH', true);
}

if (!class_exists('ShellDispatcher')) {
    ob_start();
    $argv = false;
    require CAKE . 'console' .  DS . 'cake.php';
    ob_end_clean();
}

Mock::generatePartial('ShellDispatcher', 'UsoShellMockShellDispatcher', array(
    'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment'
));

class UsoShellTestCase extends CakeTestCase {
    
    function setUp() {
        $this->Dispatcher =& new UsoShellMockShellDispatcher();
        $this->Shell =& new UsoShell($this->Dispatcher);
        $this->Shell->Dispatch = $this->Dispatcher;
        $this->Shell->Dispatch->shellPaths = Configure::read("shellPaths");
    }
    
    function tearDown() {
        unset($this->Shell, $this->Dispatcher);
        ClassRegistry::flush();
    }
    
    // テストメソッド
    function testMain() {
        $this->Shell->Dispatch->expectAt(0, "stdout", array("uhouho\n", false));
        $this->Shell->main();
    }
}
?>

実行方法と出力結果

$ cd c:/xampp/htdocs/cakephp/app
$ ../cake/console/cake testsuite app case shells/uso


Welcome to CakePHP v1.3.2 Console
---------------------------------------------------------------
App : app
Path: c:\xampp\htdocs\cakephp\app
---------------------------------------------------------------
CakePHP Test Shell
---------------------------------------------------------------
Running app case shells/uso
1/1 test cases complete: 1 passes.
Time taken by tests (in seconds): 0.022739887237549
Peak memory use: (in bytes): 12,622,648

解説

uso.test.phpの先頭部分で何をやっているのかわかりづらいと思います。要はモックオブジェクトを使って、Dispatcherの書き換えと、CLIへの入出力の無効化をしています。あとはそのモックをsetUp()内で読み込んでいます。詳しいことは下記の「参考」にあるアドレスを見てください。

また、「expectAt()」というメソッドはSimpleTestのアサーションの一つです。詳しいことはSimpleTest for PHP mock objects documentationを参考にしてください。

地味な注意ですが、expectAt()で予期している文字列は “uhouho” ではなく “uhouho\n“です。$this->out()は改行記号付きで標準出力するメソッドです。何回やってもfailになるのでおかしいと思い、Coreのテストを見てやっと気付きました。

参考

  1. Shellクラス内のメソッドをテストしたかったので大変参考になりました。 uso.test.php の setUp()メソッドで、KwrankShell となっているところは UsoShell にするのが正しいようですね。

  2. ご指摘ありがとうございます。修正しました。

    こんな記事書いておいてなんですが、最近はコマンドラインスクリプトをフレームワークを用いて作ることは辞めました。ディスパッチャの解釈や、モックを作ったりが面倒だからです。それに、コピペでないと再現できないおまじないは鬱陶しいですしね。

    MVCの考え方はCLIアプリでもそっくり適用できそうなもんですが、現状のフレームワークはWEB表示のことしか考えていないようです。

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>