テストケースを作成する際に、拡張元となる CakeTestCase を拡張します。
CakeTestCase を拡張した独自のクラスを作成することで、
共通の処理を追加することができます。
しかし、残念ながら、
CakeTestCase クラスの読み込みには、
app_controller.php のような中継するクラスは用意されていません。
そのため、
共通処理を書いた CakeTestCase の拡張を読み込む場所が少々面倒なことになります。
- [app/tests/cases/controllers/users_controller.test.php]などの先頭で、毎回読み込むのが面倒
- [app/config/bootstrap.php]に、テストための処理を書くのは憚られる
- コアハックは論外
ので、その拡張クラスを読み込むテスト用の bootstrap.php 的なものをまず作ります。
CakeTestCase クラスは、
app_controller.php のような空っぽのクラスは用意されていません。
前提 & 環境
- テストを実行するのは、コンソール「cake testsuite app all」です。
- example.com/test.php の web経由のアクセスは対象外です。
- 作業環境
- 意識する対象
- PHP5.2 以上の動作を意識します。
- PHP5.2 でのチェックはしていませんが、PHP5.3 の機能は使用していません。
- PHP5 の機能(アクセス権など)は、使用します。
- CakePHP 1.3 以上の動作を意識します。
- 安定化・高速化が図られたバージョンを選択しています。
- PHP5.2 以上の動作を意識します。
参考 : CakeTestCase クラスの読み込み場所
[cake/tests/lib/test_manager.php]
<?php /** * Includes the required simpletest files in order for the testsuite to run * * @return void * @access public */ function _installSimpleTest() { App::import('Vendor', array( 'simpletest' . DS . 'unit_tester', 'simpletest' . DS . 'mock_objects', 'simpletest' . DS . 'web_tester' )); // ここで基礎となるクラスを読み込みます。 require_once(CAKE_TESTS_LIB . 'cake_web_test_case.php'); require_once(CAKE_TESTS_LIB . 'cake_test_case.php'); }
CAKE_TESTS_LIB の定義
<?php /** * Path to the core tests directory. */ if (!defined('CAKE_TESTS')) { define('CAKE_TESTS', CAKE.'tests'.DS); } /** * Path to the test suite. */ define('CAKE_TESTS_LIB', CAKE_TESTS.'lib'.DS);
解説
本題と同時に、コンソールでのテストを実行前までも解説します。
「cake testsuite app all」を例とします。
[cake/console/cake] から、[cake/console/cake.php] に振り返られます。
ShellDispatcher::ShellDispatcher が呼ばれ、
コンソールを操作する上で必要なファイルを読み込みます。
[cake/console/cake.php]
<?php function ShellDispatcher($args = array()) { set_time_limit(0); $this->__initConstants(); $this->parseParams($args); $this->_initEnvironment(); $this->__buildPaths(); // メイン処理は、$this->dispatch() が行います。 $this->_stop($this->dispatch() === false ? 1 : 0); } function dispatch() { // 略 // schema 処理なら、SchemaShell クラスインスタンスを、 // testsuite 処理なら、TestSuiteShell クラスインスタンスを、 // というように、その処理用のクラスを読み込みます。 // ここでは、 // TestSuiteShell クラスインスタンスを取得します。 // また、$plugin は 「null」です。 $Shell = $this->_getShell($plugin); // 略 if (method_exists($Shell, 'main')) { // 専用クラスの処理を開始します。 $Shell->startup(); return $Shell->main(); } // 略 }
$Shell = $this->_getShell($plugin) を詳しく見ます。
[cake/console/cake.php]
<?php function _getShell($plugin = null) { // $this->shellPaths には // app や cake 、plugin などにあるシェル用ディレクトリへのパスが格納されています。 // cake より app のディレクトリパスが前にあるので、 // パスに目的のファイルがあった場合、そちらが優先されることになります。 // 例 : // array( // '/path/app/plugins/debug_kit/vendors/shells/', // '/path/app/vendors/shells/', // '/path/vendors/shells/', // '/path/cake/console/libs/', // ) // // $this->shell には、 実行するシェルの種類が格納されています。 // ここでは、「testsuite」です。 // $this->shellClass には、 実行するシェルのクラス名が格納されています。 // ここでは、「TestsuiteShell」です。 foreach ($this->shellPaths as $path) { // パスを作成します。 $this->shellPath = $path . $this->shell . '.php'; // プラグインは、ここでは無視します。 $pluginShellPath = DS . $plugin . DS . 'vendors' . DS . 'shells' . DS; // プラグイン部分は、ここでは、「true」が返ります。 // 作成されたパスにファイルが存在する場合、ループが止まります。 // ここでは、なにもしなければ、最終的に、 // [/path/cake/console/libs/testsuite.php] でループが止まりますが、 // [/path/app/vendors/shells/testsuite.php] や、 // [/path/vendors/shells/testsuite.php] が存在する場合、 // そちらでループが止まるようになります。 // // [/path/app/vendors/shells/testsuite.php] を作成することで、 // app 側が読み込まれるようになり、再度、[/path/cake/console/libs/testsuite.php] を読み込むことで、 // bootstrap.php 的な使い方ができるようになります。 if ((strpos($path, $pluginShellPath) !== false || !$plugin) && file_exists($this->shellPath)) { $loaded = true; break; } } if (!isset($loaded)) { return false; } if (!class_exists('Shell')) { require CONSOLE_LIBS . 'shell.php'; } if (!class_exists($this->shellClass)) { require $this->shellPath; } if (!class_exists($this->shellClass)) { return false; } $Shell = new $this->shellClass($this); return $Shell; }
[/path/vendors/shells/testsuite.php] を作成します。
CakeTestCase の拡張クラスとして、
WhiskCakeTestCase クラスを作成したとします。
(Whisk : 泡立て器 -> http://www.google.com/images?hl=ja&safe=off&q=whisk&um=1&ie=UTF-8 )
[/path/vendors/shells/testsuite.php]
<?php // 元々読み込むはずだった [/path/cake/console/libs/testsuite.php] を読み込みます。 require_once CONSOLE_LIBS . 'testsuite.php'; // これはおまじないです。 // CakeTestCase クラスの読み込み前に読み込まないと // なぜか、simpletest の HtmlReporter クラスが多重定義で怒られます。 // おなじないがないと、 // [/path/cake/console/libs/testsuite.php] の __installSimpleTest メソッドの // 全く同じ記述の App::import('Vendor', 'simpletest' . DS . 'reporter') で発生します。 // CakePHP2 では、simpletest から PHPUnit にテストが変更になるので、 // 今のところ回避だけにしています。 App::import('Vendor', 'simpletest' . DS . 'reporter'); // 本命の CakeTestCase の拡張クラスを読み込みます。 // ファイルパスは、 // [/path/cake/tests/lib/cake_test_case.php] に倣って // [/path/app/tests/lib/whisk_cake_test_case.php] です。 App::import(null, 'WhiskCakeTestCase', false, TESTS . 'lib');
[app/tests/lib/whisk_cake_test_case.php]
<?php // 親クラスを読み込みます。 App::import(null, 'CakeTestCase', false, CAKE_TESTS_LIB); class WhiskCakeTestCase extends CakeTestCase { // 共通処理 }