保存時にリアルタイムテスト実行でお手軽テスト
普段、Ethnaを使用して開発しているのだが、EthnaはWeb画面でテスト結果表示なので、どうしても一手間増えてしまう。
実際にテストケースを書いてブラウザで確認するのであれば、テストケースを書かずに、書いた実コードをブラウザで動作確認していた時と変わらず、Alt+Tab、Ctrl+R とかするのであれば、実コードを実行するいままでのやり方で済ませてしまい、なかなかテストケースを書くというやり方が浸透しない。
もしくは、最初はするけど、忙しくなるとしなくなるという状況でした。
テストケースを書くことで得られる安心感や、結果としてクオリティの担保になるというのは、理解してもらえるとは思うのだが、Alt+Tab、Ctrl+R という、今までと同じ手順ではない、テストを書く方が楽。という方法が確立できればいいのではないか。なんて考えていた。
つい先日、Ethnaの今後のロードマップを考える というか 話し合う飲み会があったのだが、そこでテストの話が少し出た。
id:sotarok から、ファイルの保存時に自動でテストを実行なんて話を聞いて、いつぞやのPHP勉強会で、kunitさんのTDDの話を思い出した。
ちょうど、今回途中参加した案件では、Ethnaではなく独自フレームワークっぽいもので作成だったので、TDDをすべく環境を整えてみた。
テストライブラリに何を使うか
ばっちりテストを書くというよりは、手軽に書きたいので、limeというテストライブラリを使おうとした。
limeでなるべく気軽にユニットテストを書く - id:anatooのブログ
[PHP] limeでTDDを体験する - DQNEO起業日記
このあたりが参考になるだろうか。
SimpleTestのようにクラスを作らずに手軽に出来るのが利点だ。
ところがどっこい、今回の案件は PHP4 でした。。。
limeは当然PHP5用なので、少し書き換えてPHp4用にしようかと思ったが、テストライブラリ自体が信用できない自分が書いたコードというのは意味がない。。。
他に使えそうなのが、PHPUnit、SimpeTest、phptなどなど。
phptは、ちょっと手間がかかるので無理かも。とういことで、Ethnaで使い慣れた SimpeTest を使うことにしました。
お手軽テスト
お手軽にテストするために、テスト用にわざわざ何かを実行するのは手間なので、実行ファイルの最後にテストを書くようにした。
limeでなるべく気軽にユニットテストを書く - id:anatooのブログ
このあたりを参考にしてみる。
<?php define('BASE', realpath(dirname(__FILE__).'/../')); class ActionForm1 { //ここに実際のコード } // test if (debug_backtrace()) return; include_once BASE.'/lib/test_autorun.php'; include_once FR_BASE.'/lib/test_autorun.php'; class Test_ActionForm1 extends UnitTestCase { var $ac; function setUp() { $this->ac = new ActionForm1(); } function testPrepare() { //ここにテストコード $this->assertEqual($this->ac->hoge, 'hoge'); } }
test_autorun.php
テストの実行ファイル
<?php require_once dirname(__FILE__) . '/simpletest/unit_tester.php'; require_once dirname(__FILE__) . '/simpletest/mock_objects.php'; require_once dirname(__FILE__) . '/simpletest/collector.php'; require_once dirname(__FILE__) . '/simpletest/default_reporter.php'; register_shutdown_function('test_autorun'); /** * Exit handler to run all recent test cases if no test has * so far been run. Uses the DefaultReporter which can have * it's output controlled with SimpleTest::prefer(). */ function test_autorun() { $candidates = array(); foreach (get_declared_classes() as $class) { if (substr(strtolower($class),0,5) == 'test_') { $candidates[] = $class; } } $loader = new SimpleFileLoader(); $suite = $loader->createSuiteFromClasses( reset($candidates), $loader->selectRunnableTests($candidates)); $result = $suite->run(new DefaultReporter()); if (SimpleReporter::inCli()) { exit($result ? 0 : 1); } }
実行結果は
:!php form1.php test_actionform1 OK Test cases run: 1/1, Passes: 0, Failures: 0, Exceptions: 0 続けるにはENTERを押すかコマンドを入力してください
さらに一歩進んで
これだと、保存後にコマンドを実行する必要がある。
もう一歩進んで、保存時に自動的にテストが実行されるようにしてみる。
kansit という、rubyのスクリプトがあることを id:sotarok から教えてもらったのだが、開発しているサーバには rubyが入っておらず、rubyの環境を整えるよりは、作った方が早いので以下のようなものを作ってみた。
<?php $dirs = array(); foreach ($argv as $arg) { $dir = realpath($arg); if ($dir && is_dir($dir)) { $dirs[] = $dir; } } $files = array(); while (1) { usleep(500); $target = array(); foreach ($dirs as $dir) { foreach (glob($dir.'/*.php') as $file) { if (isset($files[$file]) && $files[$file] != filemtime($file)) { $target[] = $file; } $files[$file] = filemtime($file); } } if (count($target)) { exec('reset'); foreach($target as $file) { echo ">>run test {$file}\n"; //passthru('php -l '.$file); passthru('php '.$file); echo "\n"; } } }
コマンドラインで実行し、引数に監視対象のディレクトリを指定するというスクリプトです。
無限ループで監視を続けるので、停止をするときには、Ctrl+C で止めてください。
開発する環境
screen を縦分割します。
上の画面を 10行ほどにして、そこで、上記の監視スクリプトを実行しておきます。
で、下の画面で普通にコードを書いて、保存をした瞬間に上の画面で結果が表示されます。
Erase is backspace. >>run test /path/to/form1.php test_actionform1 OK Test cases run: 1/1, Passes: 0, Failures: 0, Exceptions: 0
こんな感じ。
保存時に毎回見るわけではなく、気にせずがつがつ書いていけばいい。
PHPの構文エラーもすぐに気づくし、通ると思った瞬間に確認すればいい感じです。
それに、画面上部は常に視界の片隅に入るので、結果がNGの場合に長い行が出るので、気にしていなくても目に入ってくる。
ちょっとしたことだが、コードを書く作業の妨げにならずに何度もテストが実行され、コードが正しく動くというのに自信が持てるので、作成中多少手戻りが発生しても、影響範囲とかを考えたり悩んだりしなくて済むのが楽だ。