Test::Harness モジュールに、単独で Test::Harness 用のテストファイル(群)を実行できる prove というツールがついているのを発見。
スクリプトのちょっとしたリグレッションテストを書いたりするのにこれはいい。 ダミーのパッケージを作る(autoconfiscate するとか Makefile.PL 作る)とか、自前でスクリプトを書くとかしないで済む。書きなれたスタイルでテストコードが書けるし。
tpage (Template Toolkit) 用テンプレートをいじる時にリグレッションテストをかけられるようにしたかったで、使ってみることにする。
私が Perl が好きな理由の一つに、標準でExtUtils::MakeMakerという Makefile ジェネレータがついているところである。これを使って Makefile.PL を書くと
perl Makefile.PL make manifest make make test make dist
で <pacakge>-<versionno>.tar.gz というソースパッケージを作ることができ、
tar zxvf <pacakge>-<versionno>.tar.gz cd <pacakge>-<versionno> perl Makefile.PL make make test make install
という手順でインストールする事ができるようになる。 パッケージの作り方が確立されているので、容易に新しいパッケージを開発しはじめられる。
逆に Java でプログラムを書くのが億劫なのは、このあたりの準備が面倒だからである。 Ant を使っても結局ここら辺自分でやらなければならないし。
ちょっとしたパッケージを作りたいと思うことは良くある。 例えばいくつかのデータファイルと、READMEファイルなんかをひとまとめにしてアーカイブにするとか。
本当にちょっとしたであれば、手動でアーカイブすれば良いのだが、
あたりを考慮しなければならない時は面倒くさくなってくる。
自動化としては
あたりがぱっと思い浮かぶ。 しかし、最初の2つは毎回同じようなものを書くのが面倒だし保守もしにくい。 GNU Autotools はちょっとごっつすぎだし、Windows での環境構築も面倒。
ということで最初は ExtUtils::MakeMaker を使うという線で考えてみた。 もともと Perl モジュール用で汎用用途にはちょっと邪魔な振舞いもあるが、使えないことはないと思う。 しかし make (GNU Make あるいは nmake など) に依存しているという欠点がある。
ということで ExtUtils::MakeMaker の代替である Module::Build ベースで汎用用途に使えないか検討してみた。こちらは pure Perl で make を必要としない。
Module::Build も Perl モジュールビルドにあわせた振舞いがあるものの、ちょっとカスタマイズすれば使えそうだ。 で、いろいろいじった結果、次のような感じにすると使いやすそうだ。
use Module::Build; my $class = Module::Build ->subclass(class => 'NonmoduleBuilder', code => q{ # Don't make blib sub ACTION_code {}; # Don't make blib sub ACTION_docs {}; # Don't make META.yml sub ACTION_distmeta { # no warning on ACTION_distdir $_[0]->{metafile} = 'MANIFEST'; }; # Don't add MEATA.yml to MANIFEST sub ACTION_manifest { $_[0]->{metafile} = 'MANIFEST', $_[0]->SUPER::ACTION_manifest(@_); }; }); # Set your archive name and version. $class->new(dist_name => 'mypackage', dist_version => '1.0.2', )->create_build_script;
カスタマイズした部分は以下(Module::Build 0.26 で動作確認)。
上記のようなファイルを Build.PL という名前で作っておけば
perl Build.PL ./Build manifest ./Build ./Build test ./Build dist
等として、アーカイブ作成が容易にできるようになる。
しばらくこの方法でいろいろ試してみることにしてみよう。
CGI プログラムを書いていて、いつも困るのがリグレッションテスト。
パッケージのビルド時に実行するテストスーツ (make check / make test 用テストプログラム群) に含めておきたいが、さすがにその場で Web サーバの下へセットアップするわけにもいかない。 ミニ Web サーバを同梱してテストスーツ内で起動する方法はちょっとおおがかかりだし、ポート番号の選択やらサーバの停止の問題もあって、かなり面倒。
結局、テストスーツの中で環境変数や標準入力など CGI リクエスト環境をセットアップして、CGI プログラムを実行するという王道(?)かつ泥臭いテストを書くことになったりする。
何かいいものはないかと探していたところ、WWW::Mechanize::CGI というものをみつけた。
LWP::UserAgent を継承した WWW::Mechanize モジュールは Web ブラウジングを容易にする有名どころのモジュールである。
WWW::Mechanize::CGI モジュールはさらにこれを拡張したモジュールで、HTTP リクエストを、仮想的に CGI プログラムやサブルーチンへの呼出しにしてくれる。 これを用いるとあたかも Web サーバ上の CGI プログラムにリクエストしレスポンスを受けとっているかのように、テストプログラムを書くことができる。
素晴しい。
さっそく WiKicker のテストを書き換えてみた:
use Test::More tests => 2; use WiKicker::WikICGI::Controller; use WWW::Mechanize::CGI; use File::Temp qw(tempdir); use File::Spec; my $www_dir = tempdir(CLEANUP => 1); my $mech = WWW::Mechanize::CGI->new; $mech->cgi(sub { $ENV{PATH_INFO} = '' if $ENV{PATH_INFO} eq '/'; WiKicker::WikiCGI::Controller->new->run}); $mech->env($mech->env, SCRIPT_FILENAME => File::Spec ->catfile($www_dir . '/wiki'), SCRIPT_NAME => '/wiki'); my $response = $mech->get('http://localhost/wiki'); ok($response->is_success); like($response->content, qr|<title>WikiForum\[WiKicker\]: FrontPage</title>|);
WWW::Mechanize::CGI オブジェクトを new した後、cgi メソッドで CGI サブルーチンを指定するか、cgi_application メソッドで外部 CGI プログラムを指定する。 ここでは直接、CGI サブルーチン (WiKicker::WikiCGI::Controller->new->run を実行)を指定した。
なおここで WWW::Mechanize::CGI が使っている HTTP::Request::AsCGI 0.5 における PATH_INFO の扱いが Apache などとは違って、空でも必ず '/' が入るようになっている。 これだと WiKicker では困るので、サブルーチンのところで修正している。
後は必要ならば WWW::Mechanize::CGI::env で、追加の環境変数設定を行っておく。
セットアップが済めば通常の WWW::Mechanize と同様に get 等でリクエストを行いレスポンスを受けとることができるようになる。
いい。しばらく試してみて不具合がなさそうなら、定番のテストスタイルにしたい。
ちなみに Test::Harness 用の Test::WWW::Mechanize にあわせて、Test::WWW::Mechanize::CGI というものもある。 これらを用いるとさらにテストを書くのが楽になるが、依存するモジュールも多いので無理に使わないほうがいいかもしれない。
テストファースト開発に慣れてしまうと、テストコード無しにプログラムを書くというのは不安でたまらなく感じてくる。
テストが欲しい。安らぎが欲しい。
今開発している WiKicker ベースの Web アプリケーションもだんだん機能が増えてきて、コードを触るのがコワくなってきた。
今回は Basic 認証等もあるので、WWW::Mechanize::CGI ではなくてきちんと deploy してから Test::WWW::Mechanize でテストすることにした。
Test::WWW::Mechanize、使ってみると WWW::Mechanize + Test::More よりテストを書くのも読むのも楽になった。
deploy が必要なリグレッションテストはさすがに t/ の下に入れておくのはどうかと思う。 プロジェクト的にはビルドサーバを用意して、そこで自動的にテストできるような環境を用意するのが良さそうだ。
自分がプログラムをスクラッチから書き直したいと思った時、またスクラッチから書き直したいと言われた時のためにまとめておこう。
スクラッチから書き直したいと思う理由はだいたいこうだ。
どんなプログラムでも開発が進み詳細がわかってくると、こうしておけば良かったと思う点がでてくるものだ。
さらに、他人が書いたプログラムだとよく分からない。
It's harder to read code than to write it. (プログラムというのは書くより読むほうが難しい。) — Things You Should Never Do, Part I - Joel on Software
いっそ作り直してしまいたいと思うのはどの開発者でもあることだ。
しかし多くの場合スクラッチから書き直すことはリスクとデメリットだらけだ。
ほとんどの場合は、漸進的に今のプログラムを修正・改良していった方が得策なのだ。
そうはいってももちろんスクラッチから書き直した方が合理的な場合もある(書き直してはいけない場合も書き直した方が合理的だと思ってしまうわけではあるが)。
それは次のような場合だろう。
本当にスクラッチから書き直した方がよい場合は止める理由はない。
さてこの記事をスクラッチから書き直したいと思う時がきませんように。
Naney (なにい) です。株式会社MIXIで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。