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

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

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

計画的に実行するのはよいことですが

前回も紹介したように,Test Anything Protocolでは「これから10個のテストを実行します」と宣言する場合はこのように書くことになっていました。

use strict;
print "1..10\n";  # 宣言部
for (1..10) {
    print "ok $_\n";
}

このような宣言部の存在は,テスト結果をパースして分析するTest::Harnessのようなツールにとっては非常に便利なものですが,たとえば環境によってテストの数がかわるとき,あるいはテストファイルが非常に長くなってきたとき,はたまた多くの人が平行してファイルやテストの追加作業をしているため最後にマージするまでテストの数がわからないとき,事前にテストの数を把握していなければならないというのは,大きな制約にもなりえます。

単純そうに見えるTest Anything Protocolも,この20年の間に10回以上の改訂を経て,少しずつ柔軟性を増してきました。今回はその流れを簡単に追いかけてみることにしましょう。

skip_all

Perl 1.0の時代から使い続けられてきたTAPに最初の拡張が行われたのはPerl 5.2のベータリリースが行われていた1996年のことでした。その最初の拡張では個々のテストの終了ステータスをチェックするようになり,#で始まる行が出力された場合はコメントとして無視するようになったのですが,この年にはもうひとつ,テストの数を0と宣言することでテスト全体をスキップするという機能が追加されました。

use strict;
print "1..0\n";
exit;

これはいまでも特定の機能,特定の外部アプリケーションがないのでテストが実行できないとか,単に時間がかかるテストなのでふだんは省略したいという場合によく使われているのですが,planに直接0を指定するのは20世紀のやり方。

use strict;
use Test;
if ($^O eq 'MSWin32') {
    plan tests => 0;
    exit;
}
plan tests => 1;
ok 'tests for unix';

いまどきのTest::Moreでは意図しないところでテストがスキップされてしまうのを防ぐためtestsに0を指定することはできなくなっているので,plan skip_allを使います。

use strict;
use warnings;
use Test::More;

eval 'require Some::Module';
plan skip_all => 'this test requires Some::Module' if $@;

unless ($ENV{RELEASE_TESTING}) {
    plan skip_all => 'set RELEASE_TESTING to test';
}

skip

ただし,このようにテストファイル全体をスキップしてしまうのはかならずしも最善の方法とは限りません。大きなテストのごく一部のテストのみスキップしたい場合は,ファイルを分けるより,その部分のみスキップできるようにしたほうが効果的です。そのため,翌1997年にはこのように個々のテスト単位でスキップできるような仕掛けが用意されました。

use strict;
use Test;
plan tests => 2;
skip('this test fails', 1 == 0);
ok 'portable test';

いまどきの書き方ならこうなりますね。

use strict;
use warnings;
use Test::More tests => 2;
SKIP: {
    skip 'this test fails', 1;
    ok 1 == 0;
}
pass 'portable test';

スキップするといっても,単にテストを飛ばすだけでは最初に宣言したテストの件数と実行件数が一致せず,テストに異常があったとみなされてしまうので,実際にはテストを実行するかわりにスキップするテストの分だけokを出力して数あわせをしています。proveやmake testを通さずに直接perl skip.tしてみると,様子を確認できます。

> perl skip.t
1..2
ok 1 # skip this test fails
ok 2 - portable test

todo

skipは上手に使えば便利な仕組みですが,テストが失敗するからといってなんでもかんでもスキップしてしまうと,その後なんらかの事情でテストが失敗しなくなったときに(テストを実行していないので)気づけない,という問題が起こります。そのため,失敗は失敗として受け入れたうえでテスト全体の結果には影響を与えないようにしようというのが1998年に導入されたtodoの仕組みです。

当初のtodoは,planを指定するときに失敗してもかまわないテスト番号をあわせて指定するようになっていました。

use strict;
use Test;
plan tests => 2, todo => [1];
ok 0;
ok 1;

このテストを実行すると,ひとつめのテストは失敗するものの,それは既知のTODOであるとして無視され,全体としてはテストに通っていると判断されます。

> prove todo.t
todo.t .. # Failed test 1 in todo.t at line 4 *TODO*
#  todo.t line 4 is: ok 0;
todo.t .. ok
All tests successful.

Test Summary Report
-------------------
todo.t (Wstat: 0 Tests: 2 Failed: 0)
  TODO passed:   2
Files=1, Tests=2,  0 wallclock secs ( 0.03 usr +  0.03 sys =  0.06 CPU)
Result: PASS

ただし,この書き方はいかにも面倒ですし,大きなテストや複雑なテストでは番号を把握するのも一苦労です。そのため,Test::Moreが使われるようになるとこの方式は廃れ,紆余曲折を経て,いまのようなブロックを利用した書き方にとってかわられます。

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

TODO: {
    local $TODO = 'not implemented';
    fail 'todo';
}
pass 'ok';

こちらの書き方の場合,影響範囲をブロックでくくるところまではskipと同じですが,スキップするテストの件数を指定しなくてもよいのが大きなポイント。いちいちテストを修正しなくても,モジュール(や,その他の環境)を直していけば順次テストが通るようになるので,テスト駆動開発もしやすくなりました※1)⁠

※1

ただし,skipと違ってtodoは実際にテストを実行するため,本当にそこで処理が止まってしまうようなエラーが出る場合はtodoではなくskipにしておく必要があります。また,Test::Moreには単なるskipと区別できるように,todo_skipというコマンドが用意されていることも覚えておくとよいでしょう。

著者プロフィール

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

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

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

コメント

コメントの記入