yamaokaです。
何かソースコードを書いた場合、皆さんはどのようにテストをしていますか?
PHPの場合、PHPUnitやSimpleTestを使ってユニットテストをすることが多いと思います。でも、ユニットテストのために新しいファイルを作ってメソッドを実装したりするの、面倒くさくないですか?
Pythonには標準でDocTestという仕組みが付いています。詳細はPythonのドキュメント(日本語)を参照してもらうことにして、簡単に言うと、実際のソースコードの中にコメントとしてテストケースを書いてしまおうというアイデアです。
「実際のソースコード=テストケース」になるのですから、メソッドを修整したからテストケースも修整して…という手間が少なくて済みますよね。そうしたDocTestの仕組みを、PHPに移植しようとする試みを紹介します。
rhacoのDocTest
rhacoは「PHPのライブラリパッケージ/ セットアップアプリケーション」です(公式サイトより)。rhacoのソースコード自体にDocTestが記述されているので、サンプルとして見るにはちょうどいいでしょう。webアプリケーションフレームワークとして利用する以外に単にライブラリとして利用することもできるので、rhacoを利用していないソースコードのDocTestをrhacoを使って行うことも可能です。
さっそくDocTestを試してみましょう。次のようなFooクラスを用意します。
<?php
class Foo
{
function bar()
{
/*** eq('bar', Foo::bar()); */
return 'bar';
}
function baz()
{
/*** #pass */
}
}
「
/*** … */
」で囲まれた箇所がDocTestのテストケースとして処理されることになります。今はテストしないとか、テストコードをまだ書いていない場合は「#pass」と書いておけばスキップされます。
次に、テストを実行するためのtest.phpを準備します。
<?php
require_once 'path/to/rhaco/Rhaco.php';
Rhaco::import('util.DocTest');
DocTest::execute('Foo.php');
そして、test.phpを実行してみます。
$ php -q test.php
success: 1 / fail: 0 / pass: 1 / none: 0 / all: 2
(Foo::baz:line. 9)PASS Foo.php
テストが実行されましたね! 試しにFoo::barのテストコードを「
/*** eq('barrrr', Foo::bar()); */
」に書き換えて実行しなおすと、次のようにきちんとエラーになります。$ php -q test.php
success: 0 / fail: 1 / pass: 1 / none: 0 / all: 2
(Foo::bar:line. 4)FAIL expectation [string(6) "barrrr"
] : Result [string(3) "bar"
]
(Foo::baz:line. 9)PASS Foo.php
値の一致をテストする「eq」コマンド以外にもテスト用のコマンドが用意されているので、詳しくはrhacoのソースかドキュメントを参照してください。
その他のDocTest
その他のDocTestの試みとしては、現在次のバージョンが開発されているwebアプリケーションフレームワーク、MapleのDocTest導入の試みがあげられます。また、同じくwebアプリケーションフレームワークのsymfonyでも、sfDocTestPluginが開発されています。
PHPのライブラリ集、PEARでは、まだアルファ版ですがTesting_DocTestがリリースされています。
最後に
rhacoのDocTestを中心に紹介しましたが、いかがでしょうか。テストコードを記述する負担をできるだけ軽くする試みとして、DocTestは面白いと思います。反面、ソースコード中にテストコードが混在するので、ソースコードが見づらくなるといった欠点もあります。そうした場合、従来のユニットテストと併用するのも手ですね(rhacoの場合、DocTestからユニットテストを実行できます)。いろいろなテストコードの書き方がありますが、選択肢の一つとして考えてもよいのではないでしょうか。