前回の
テストモジュールの作り方
たくさんのテストを書いていくと、
テスト関数を関数内から呼び出すと期待した動作とならない
実際のテストでは、
そうなってくると、
たとえばJSONデータに指定したキーが存在することをテストするために、JSON::PPのdecode_の呼び出しとis関数をまとめてtest_を作ったとします
1: use Test::More tests => 1;
2: use JSON::PP;
3: sub test_json_key {
4: my ($json, $key, $val, $name) = @_;
5: my $ref = decode_json($json);
6: return is($ref->{$key}, $val, $name);
7: }
8: my $data = q#{ "address": "Gotanda" }#;
9: test_json_key(
10: $data, 'address',
11: 'Kichijoji',
12: 'address test'
13: );
リスト2を実行すると実際には9行目でテストが失敗がしますが、Failed test 'address test' at test_と出力され、Test::Moreにはisを別の関数でラップしたことを知る手段がないためです。
Test::Builderを使って書きなおす
実はTest::Moreをはじめとしたほとんどのテストモジュールは、Test::Moreに付属するTest::Builderというモジュールを使って作られています。先ほどの関数もTest::Builderを使って書きなおすと正しい結果が得られます
1: use Test::More tests => 1;
2: require Test::Builder;
3: use JSON::PP;
4: sub test_json_key {
5: my ($json, $key, $val, $name) = @_;
6: my $ref = decode_json($json);
7: my $tb = Test::Builder->new;
8: return $tb->is_eq($json->{$key}, $val, $name);
9: }
10: my $data = q#{ "address": "Gotanda" }#;
11: test_json_key(
12: $data,
13: 'address',
14: 'Kichijoji',
15: 'address test'
16: );
リスト3の8行目で使用しているis_はTest::Builderが提供するテストメソッドで、Test::Moreのis関数と同じ機能ですTest::Moreのis関数の内部では、is_を呼び出しています)。
今度はエラーメッセージにFailed test 'address test' at test_と出力され、
このように、Test::Builderのメソッドを呼び出すことで独自のテスト関数を作ることができます。また、
package Test::JSON::Key;
use parent qw/Exporter/;
our @EXPORT = qw/test_json_key/;
sub test_json_key {
...
}独自のエラーメッセージを表示する
Test::Builderにはokメソッドという、Failed [test name] at [test file]line [n].というメッセージは、
diagメソッドと組み合わせて次のようにテスト関数を定義すれば、okメソッドや、is_メソッドを内部で呼び出し、
if ($pass) { # テストが成功したとき
$tb->ok(1, $name);
} else { # テストが失敗したとき
$tb->ok(0, $name);
$tb->diag("got: $got");
$tb->diag("expected: $expected");
}テストの数を指定する
Test::Moreではテストの数をuse Test::More tests => 42といった形式で指定できますが、Test::Builderと一緒に提供されているTest::Builder::Moduleを使います。
package Test::MyTestModule;
use parent qw/Test::Builder::Module/;
our @EXPORT = qw/my_test/;
sub my_test { ... }このようにTest::Builder::Moduleを継承すると、Test::More自体もTest::Builder::Moduleを継承しています)。
use Test::MyTestModule tests => 42;Test::Builder::ModuleはExporterモジュールを継承しているので、@EXPORTで指定したmy_関数がエクスポートされ、
Test::Moreの解説でも触れましたが、done_を使って最後にテスト数をカウントする方法がお勧めですが、Test::Builder::Moduleを使って両方のスタイルに対応できるようにしておくのが良いでしょう。
Test::Testerによるテスト結果のテスト
テストモジュールにも当然テストは必要です。テストの結果は標準出力と標準エラー出力にTAP形式で出力されるので先述したTest::Outputを使ってのテストもできますが、Test::Testerというモジュールを使うともっと簡単にテスト結果のテストができます。
Test::Testerは、Perl 5.よりコアモジュール入りしたテストモジュールです。Perl 5.より前のバージョンを使っている場合は、1.以降のTest::MoreをCPANからインストールすると使えるようになります。
Test::TesterではTest::Builderの主要なメソッドを置き換え、is関数の挙動をテストするテストはリスト4のようになります。
use Test::Tester;
use Test::More;
check_test(
sub {
is(1, 2, "failure test");
diag("test end");
},
{
ok => undef,
name => "failure test",
diag => " got: '1'\n" .
" expected: '2'\n" .
"test end"
},
'is test'
);
done_testing;
また、run_という関数も用意されています。
my ($premature, @results) = run_tests(
sub {
is(1, 1, "success test");
is(1, 2, "failure test");
}
);
is(
$result[0]->{name},
"success test",
"name test"
);テストが開始される前に出力された文字列は$prematureに入り、@resultsに入ります。あとはリスト4のcheck_と同様にokや、name、diagといったハッシュキーで結果を取り出し、
このようにTest::Testerを使えばテスト単位で結果がオブジェクトとして取り出せるため、
<続きの