CakePHPのシェルの単体テストは少々面倒くさいです。WEB表示を前提としたコントローラやコンポーネントと違ってCLIへの出力をするので、CakePHP内のリクエスト順が違います。そのためDispatcherの設定などを変えなければなりません。
ただ、深く考えずにシェルのテストをしたいだけなら、Coreのテストからソースをコピーするだけで動きます。
以下に簡単なサンプルを載せます。
### サンプル
テスト対象のシェル: **uso.php**
1 2 3 4 5 6 7 8 9 |
// app/vendors/shells/uso.php <?php class UsoShell extends Shell { function main() { $this->out("uhouho"); } } ?> |
テストケース: **uso.test.php**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
// 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(); } } ?> |
実行方法と出力結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ 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](http://www.lastcraft.com/mock_objects_documentation.php “SimpleTest for PHP mock objects documentation”)を参考にしてください。
地味な注意ですが、expectAt()で予期している文字列は “uhouho” ではなく “uhouho**\n**”です。$this->out()は改行記号付きで標準出力するメソッドです。何回やってもfailになるのでおかしいと思い、Coreのテストを見てやっと気付きました。
### 参考
* [Unit Testing CakePHP Shells | Mark Story](http://mark-story.com/posts/view/unit-testing-cakephp-shells “Unit Testing CakePHP Shells | Mark Story”)
* [翻訳: Unit Testing CakePHP Shells | 1000g](http://blog.1000k.net/2010/07/27/cakephp%e3%81%ae%e5%8d%98%e4%bd%93%e3%83%86%e3%82%b9%e3%83%88/ “翻訳: Unit Testing CakePHP Shells | 1000g”)
* [CakePHPの典型的なリクエスト :: CakePHPの基本原則 :: マニュアル :: 1.3コレクション :: The Cookbook](http://book.cakephp.org/ja/view/898/A-Typical-CakePHP-Request “CakePHPの典型的なリクエスト :: CakePHPの基本原則 :: マニュアル :: 1.3コレクション :: The Cookbook”)
* [SimpleTest for PHP mock objects documentation](http://www.lastcraft.com/mock_objects_documentation.php “SimpleTest for PHP mock objects documentation”)
Shellクラス内のメソッドをテストしたかったので大変参考になりました。
uso.test.php の setUp()メソッドで、KwrankShell となっているところは UsoShell にするのが正しいようですね。
ご指摘ありがとうございます。修正しました。
こんな記事書いておいてなんですが、最近はコマンドラインスクリプトをフレームワークを用いて作ることは辞めました。ディスパッチャの解釈や、モックを作ったりが面倒だからです。それに、コピペでないと再現できないおまじないは鬱陶しいですしね。
MVCの考え方はCLIアプリでもそっくり適用できそうなもんですが、現状のフレームワークはWEB表示のことしか考えていないようです。
I have some function in Console/Command directory and i want to test these functions.Any help ?