年末前後からApache::Testで真面目にテストを書き始めている。
ところが全くドキュメントがないんだね。いや、あるんだけど、携帯の機能を使いたいだけなのにあの厚いマニュアル読むの?みたいな感じで。まぁというわけでとにかくざくざくっとドキュメント読んでテスト書いてたらようやくなんとなく分かってきた。この間ある程度他人にも説明できたので自信ついたしw
Apache::Testはmod_perlハンドラをテストするときに使う。ディストリビューション等でmod_perlをテストしようと思うとApache自体を立ち上げたりなんだりしなければいけなくて「一体どうテストすればいいんじゃ?!」となりがちだが、Apache::Testを使うと、
- テスト用httpd.confの生成
- テスト用のapacheの起動/停止
- mod_perlハンドラ上で動かしたTest::More等の結果をtスクリプト内で評価
- 追記:mod_perl1/mod_perl2両方対応している
これだけの事を行うのに色々と手続きが必要だと思いがちだが、そこはそれ、Laziness is a virtueと言い切るPerl界のハッカー達が作ったフレームワークである。超絶簡単。
まず、Makefile.PLの中身を書き換える。残念ながらModule::Installのようなクールなヘルパーは使えず、Apache::TestMMというExtUtils::MakeMaker互換のものを使う必要がある。書くのは以下のような内容:
use strict;
use ExtUtils::MakeMaker;
use Apache::TestMM qw(test clean);
Apache::TestMM::filter_args();
Apache::TestMM::generate_script('t/TEST');
my %INFO = (
NAME => 'MyApp',
...
);
WriteMakefile(%INFO)
これだけ。Apache::TestMMを呼び出して、filter_args()/generate_script()を呼ぶ。
上記のMakefile.PLを実行するとt/TEST.PLというファイルをパース、t/TESTというスクリプトを作成する。それとMakefile内にこのTESTを使ってテストを走らせるための設定がされる。
TEST.PLというファイルはもう定型でよい:
use Apache::TestRunPerl;
Apache::TestRunPerl->new->run(@ARGV);
以上!あとはmake testするとApache::Testがよきようにはからってくれる。すばらしい。
さて、テストの中身だが、これは何をテストするかによってちょっと変わってくる。
まずハンドラ内で何かをテストする場合。例えばハンドラ内でなにかのオブジェクトを作成し、メソッドを実行した結果をテストする場合。その場合はHTTPリクエストを送るクライアント側はリクエストを送る以外何もする必要がない。普通だったら、
use strict;
use LWP::Simple;
get('http://localhost:8529/path/to/url');
みたいな.tファイルを書きたくなるが、まずテスト用Apacheのポートをハードコードしたくないし、それにそこはそれ、Laziness大好きっ子だから、ハンドラを書いたらただリクエストを送るだけのファイルなんて書きたくないよね?そういう場合はt/responseというディレクトリを作って、その中に適当にハンドラを突っ込む。すると、.tファイルを全て自動生成してくれるのです!
例えばこんなハンドラをt/response/MyApp/FirstTest.pmというファイルに入れておくと
package MyApp::FirstTest;
use strict;
use Apache::Test qw(-withtestmore);
use Apache2::Const -compile => qw(OK);
use MyApp::Object;
sub handler {
my $r = shift;
plan($r, test => 2);
my $obj = MyApp::SomeObject->new();
ok($obj);
is( $obj->foo(), 'foo' );
return &Apache2::Const::OK;
}
t/myapp/firsttest.tなんていうテストファイルを作ってくれる。内容はこれだけ
use Apache::TestRequest 'GET_BODY_ASSERT';
print GET_BODY_ASSERT "/MyApp__FirstTest";
このようにハンドラを作っておくと勝手にリクエストを送るところまで自動生成してくれるわけだ。しかも嬉しい事にmake testを実行する時点でtディレクトリ内にt/confというディレクトリが作られ、その中にhttpd.confが作られる。当然このt/response内のハンドラもそこに自動登録される。自動生成されたt/conf/httpd.confにはこんな感じの記述がされ、apacheからの呼び出し可能になっている
<Location /MyApp__FirstTest>
SetHandler modperl
PerlResponseHandler MyApp::FirstTest
</Location>
make testを走らせるとクライアント側でテストフレームワークが走っているはずなのにテスト的なコードはハンドラ側にしか無い事にお気づきだろうか。実はこれ、MyApp::FirstTestの中でok()等を呼び出すとTAP形式の出力をクライアントに送り、それを自動生成されたテストスクリプトが解釈する、という仕組みになっている。
さて、これまでの説明した時点でt/ディレクトリの中はこんな感じになっている
t/response/MyApp/FirstTest.pm
t/myapp/firsttest.t # 自動生成
t/conf/apache_test_config.pm # 自動生成
t/conf/httpd.conf # 自動生成
t/conf/modperl_inc.pl # 自動生成
t/conf/modperl_startup.pl # 自動生成
基本的にハンドラのテストに関してはあとはt/response/の中にハンドラモジュールを足して行くだけである。
万が一ハンドラの設定等をhttpd.conf内から指定したい場合は上記ファイルの他にもう一つextra.conf.inというファイルを作ると良い。たとえば、MyAppVar という変数を設定したい場合は以下のようなファイルを作り、t/conf/extra.conf.inに保存する。
<Location /MyApp__FirstTest>
PerlSetVar MyAppVar "foo"
</Location>
extra.conf.inはmake test時にパースされ、一部情報が置換されたあと、t/conf/httpd.conf内からIncludeされる。これで設定が反映されるわけだ。らくちん!
ここまではハンドラ内でテスト実行する際のやりかたでした。でもハンドラ自体は作成されていて、しかも部品別に分けてさきほどまでの方法でテストできない場合、クライアント側からその動きを確かめる必要がある。その場合は先ほどのextra.conf.inにハンドラを追加登録すると良い。
<Location /path/to/myapp/anotherhandler>
SetHandler modperl
PerlHandler MyApp::AnotherHandler
</Location>
これに対して例えばt/anotherhandler.tというクライアント側のスクリプトを作る
use strict;
use Apache::Test qw(:withtestmore);
use Apache::TestUtil;
use Apache::TestRequest 'GET_BODY';
use Test::More ( tests => 1 );
use URI;
my $url = URI->new("/path/to/myapp/anotherhandler");
$url->query_form(
foo => 1,
bar => 2
);
my $data = GET_BODY $url;
ok t_cmp($data, ...., "data okay");
GET_BODY等の関数はApache::Testフーレムワークが提供してくれるユーティリティ関数。GET_BODY等は先ほどの自動生成された設定を理解しつつ(ポート番号とか)リクエストを実行する。t_cmpは様々な構造体を理解してくれるツールだ。詳しくはApache::TestUtilを見た方がよろしいな。
こうして実行されたテストのログはt/logsに格納されるので、問題があったらそちらのerror_log等を参照すると良い。
さらに、デバッグ方法としては、t/TESTを使うという手もある。Apache::Testでmake testを実行する時はt/TESTが裏方で実行されているので、テストを単体で実行する時やその他の細かい捜査をしたい場合はそのスクリプトに引数を与える。よくあるのが「テスト実行時のサーバー側のレスポンスが知りたい」という状況だ。この場合は、以下のようにしてテストを実行する。
./t/TEST -verbose t/anotherhandler.t # これで標準出力にレスポンスを表示する
さあ、以上が分かればmod_perl上で動くソフトウェアのテストもばっちりだ。もう「mod_perl用のテストなんて書けません」なんて言えませんね☆