モダンPerlの世界へようこそ

第28回 Test::More:no_planからdone_testingへ

この記事を読むのに必要な時間:およそ 5 分

no_plan

このTODOの例のように,21世紀のPerlテスト界ではなるべくテストの件数を数えなくてもすむようにという工夫がいくつも見られるようになるのですが,プロトコルそのもののほうでもその流れを助けるように,2001年4月にリリースされたPerl 5.7.1(に同梱されているTest::Harness 1.1702)からはテストの宣言部をテストの最後尾に書けるようになっています。

たとえば特定の環境ではスキップしたいテストがあったとしましょう。伝統的な書き方では最初にテストの件数を宣言しなければなりませんから,ひとまずスキップしなかった場合の数を宣言しておいて,必要に応じて空のokをはさむのが常套句でした。

use strict;
print "1..2\n";  # 宣言部
if ($^O ne 'MSWin32') {
    print "ok 1\n";
}
else {
    print "ok 1 # Skip\n";
}
print "ok 2\n";

このくらいであればスキップの機構を使わず,テストの件数を宣言するときに場合分けするやり方も考えられますが,この方式は宣言の前後に同じ条件分岐が出てきますからいかにも冗長です。

use strict;
my $total = $^O ne 'MSWin32' ? 2 : 1;
print "1..$total\n";  # 宣言部

my $count = 0;
if ($^O ne 'MSWin32') {
    printf "ok %d\n", ++$count;
}
printf "ok %d\n", ++$count;

これを21世紀風のTest Anything Protocolで書き直すと,たとえばこのように書くことができます。

use strict;
my $count = 0;
if ($^O ne 'MSWin32') {
    printf "ok %d\n", ++$count;
}
printf "ok %d\n", ++$count;
print "1..$count\n";  # 宣言部

ここではいちいち自分でカウンタの値を増やしていますが,Test::Moreなどのツールにはもともと(宣言したテストがすべて実行されたか検証するために)実行したテストの数を数える機能がついているので,それを使えばいちいちテストの数を数える必要もありません。実際に実行したテストの数を最後に宣言してやれば万事解決――となるはずでした。

no_planの問題点

ところが,Test::Moreの場合,つい最近まで,この宣言部の後置を利用するには最初に「no_plan」という特殊な宣言をしなければなりませんでした。先ほどの例をno_planを使って書き直すと,このようになります。

use strict;
use Test::More 'no_plan'; # Test::Moreの宣言部はここ
if ($^O ne 'MSWin32') {
    pass;
}
pass;
# Test Anything Protocolの宣言部はここに出力

このTest::Moreのテスト計画の宣言部と実際にTest Anything Protocolの宣言部が出力される場所のズレは,プロトコルを理解している人にとっても直感的ではなかったのですが,それ以上に,そのようなズレを実現するための仕掛けがいくつかの新しい問題を産むことになりました。

たとえば,テストが何らかの事情で途中で止まってしまったとき。本来であれば後続のテストを実行できなかったのですからテストは失敗と判断されるべきですが,no_planの場合は実際に実行したテストの数を宣言するという性質上,あたかもそれ以降のテストは最初から存在しなかったかのように,その時点までに実行したテストの数を宣言してしまいます(そのため,そこまでのテストに失敗がなければテストが通ったと判断されてしまいます⁠⁠。

あるいは,テストが途中でforkする場合。いまではTest::SharedForkというtokuhiromこと松野徳大氏がつくった便利なモジュールがあるのでそれほど問題にはならないのですが,このモジュールを使わない場合,たとえばこのようなテストは正しく動作しません。

use strict;
use Test::More 'no_plan';
if (my $pid = fork) {
    wait;
    pass "parent";
}
else {
    pass "child";
}

これは,実際にはno_planの問題ではなく,裏で使われているTest::Builder内部のカウンタが正しく増えないのが問題なのですが,たとえば自分でテスト計画を宣言すればこのような方法で回避することができます。no_planでは手の打ちようがありません。

use strict;
use Test::More tests => 2;

Test::More->builder->use_numbers(0); # テストが順不同でも許す
Test::More->builder->no_ending(1);   # child側の整合性チェックを省略する

if (my $pid = fork) {
    wait;
    pass "parent";
}
else {
    pass "child";
}

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/