本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは、
本稿のサンプルコードは、
Perlにおけるテスト
読者のみなさんはテストを書いていますか。
Perlは昔からテストをとても大事にしてきた言語です。テストを書くために必要なテストモジュールはコアモジュールとしてPerl本体と一緒にインストールされていて、
今回はテストモジュールの使い方や、
なお、Perl 5.以降で動作するように書かれており、Perl 5.で動作確認を行いました。
また、use strictと、use warningsはすべて省略していますが、
テストモジュールの使い方
まずはテストモジュールの使い方とテストの実行方法を解説します。
Test::More──Perlのテストの基本
テストモジュールの中で最も基本的な機能を提供するモジュールがTest::Moreです。Test::MoreはコアモジュールとしてPerl本体と一緒にインストールされています。
Test::Moreを使ったテスト
最初のテストとして、
1: use Test::More;
2: is(length("perl"), 4, "length test");
3: is(length(undef), 0, "undef test");
4: done_testing;
1行目は、Test::Moreを使用するための宣言です。以降で使用するisという関数が使えるようになります。
2行目は、Test::Moreのis関数を使ってテストを実行しています。is関数は引数を3つ取り、length("perl")の戻り値と、length testを指定しています。
3行目では再びis関数を使って今度はlength(undef)と、0を比較しています。なお、length(undef)は実際にはundefを返すので、
最後の4行目では、Test::Moreのdone_関数を呼び出しています。
テストの実行
リスト1を通常のPerlのプログラムと同じように実行してください。
$ perl length_test.t
ok 1 - length test ―(1)
not ok 2 - undef test ―(2)
# Failed test 'undef test' ┐
# at length_test.t line 3. |
# got: undef |―(3)
# expected: '0' ┘
1..2 ―(4)
# Looks like you failed 1 test of 2. ―(5)テストの結果が出力されました。1行ずつ見ていきましょう。
okと出力されていますが、1)isに渡したテストの名前です。
not okと出力されていますが、
length_)got)expected)
すべてのテストが終わりdone_が実行されると、1..[テスト数]という形式で出力されます。
失敗したテストがあるとテストプログラムが終了するタイミングで、
このようにPerlでは、is関数は最も基本的なテストのための関数Test::Moreが提供するそのほかの機能を解説します。
like──正規表現によるテスト
likeは正規表現を使うテスト関数です。引数の2番目に正規表現へのリファレンスを渡す点がisと異なります。
like(
"perl-5.22.0.tar.gz",
qr/perl-5\.\d+\.\d+\.tar\.gz/,
"archive name test"
);cmp_ok──任意の演算子を使った比較テスト
cmp_は指定した演算子で比較を行うテスト関数です。数値の大小比較など、
cmp_ok(1 / 10, '<', 0.11, "division test");is_deeply──リファレンスのテスト
is_はリファレンス用のテスト関数です。2つのリファレンスをたどりながら比較を行います。
sub create_person {
return { name => $_[0], age => $_[1] }
}
is_deeply(
create_person( "James", "30" ),
{ age => "30", name => "James"},
"person object test"
);リファレンスの先に一致しない箇所があるとテストが失敗し、
plan──テストの数を指定する
リスト1で用いたdone_はすべてのテストを実行したあとにテストの数をカウントする関数ですが、planという関数で最初にテストの数を指定することもできます。
42個のテストを実行
plan tests => 42;planを使わずに、Test::Moreを読み込むときに指定することもできます。
# 同じく42個のテストを実行
use Test::More tests => 42;テストの数と実際に実行したテストの数が不一致の場合、
以前はテストの数をあらかじめ指定する方法が主流でしたが、done_を呼び出す方法がお勧めです。
subtest──テストをまとめる
テストをたくさん書いていくと、subtestというしくみが用意されています。
subtestは、
1: use Test::More;
2: subtest 'group1' => sub { is(1, 1);is(1, 1) };
3: subtest 'gourp2' => sub { is(1, 1);is(0, 1) };
4: done_testing;実行すると、
# Subtest: group1
ok 1
ok 2
1..2
ok 1 - group1 ―(1)
# Subtest: gourp2
ok 1
not ok 2
# Failed test at subtest.t line 3.
# got: '0'
# expected: '1'
1..2
# Looks like you failed 1 test of 2.
not ok 2 - gourp2 ―(2)
# Failed test 'gourp2'
# at subtest.t line 3.
1..2
# Looks like you failed 1 test of 2.先にコードリファレンスの中のテストが実行され、subtest全体が成功となり、subtest全体が失敗となります。
このようにsubtestはテストのコードを理解しやすいように階層化するために使いますが、
diag──解析用メッセージを出力する
テスト関数が失敗すると原因が出力されますが、diag関数を使用します。
is($got, $expcted) or diag("DB:$db, USER:$user");テストに失敗するとテスト関数自体の戻り値は偽になるので、DBとUSERの情報が出力されます。
diag関数の出力はすべて#が先頭に付加され、
BAIL_OUT──テストを中断する
BAIL_は、
BAIL_OUT "abort the test";BAIL_が呼び出されると、BAIL_以降のコードはすべて無視されます。
Bail out! abort the testテスト結果の出力はTAP形式
リスト1で、ok、not okが出力されると説明しましたが、
- テストが成功すれば
ok、失敗すれば not okを出力する okとnot okに続けて、テストの連番とテスト名を出力してもよい (出力しなくてもよい) - テストの前か一番最後で、
1..[連番]という形式でテストの数を出力する - テストが継続できないときは
Bail out!と出力し、ただちに実行を中断する #で始まる行は、エラー内容の詳細などテスト結果を補足する情報を出力する - 上記以外の出力内容は、
TAPとしてはすべて無効となる (テストの結果には影響しない)
Perlでは、proveコマンドでテスト結果を収集、
なお、Wide character in print...という警告メッセージが出力されます。これを防ぐためには次のように、Test::Moreを読み込む前に明示的に標準出力や標準エラー出力のエンコーディングを指定します。
use open ':std', ':encoding(utf8)';
use Test::More;テストを実行し結果を収集するproveコマンド
テストを一つ一つ実行し、proveというコマンドが用意されています。
リスト1をproveコマンドで実行してみます。
$ prove length_test.t
length_test.t .. 1/?
# Failed test 'undef's length test'
# at length_test.t line 3.
# got: undef
# expected: '0'
# Looks like you failed 1 test of 2.
length_test.t .. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/2 subtests
Test Summary Report
-------------------
length_test.t (Wstat: 256 Tests: 2 Failed: 1)
Failed test: 2
Non-zero exit status: 1
...
Result: FAIL1つでもテストが失敗すると、Result: FAILと表示されるので、
次に、is(length(undef), undef,"undef test");と書き換え、
$ prove length_test.t
length_test.t .. ok
All tests successful.
...
Result: PASSTAPの詳細な出力はすべて省略され、
なお、tというディレクトリにテストを置いておけば、proveのみでまとめて実行してくれます。
次のように明示的にディレクトリの指定もできます。
$ prove testdir/このようにproveコマンドを使うことで、
そのほかのテストモジュール
Test::More以外にも、Test::Exceptionや、Test::Outputなど、
use Test::Exception;
throws_ok { die "exception msg\n" } qr/exception/;use Test::Output;
stdout_is sub { print "Hello, World" }, "Hello, World";テストモジュールを探したいときは、awesome-perlやTask::Kensho::Testingなどを参照すると、
ここまでのまとめ
(1)Test::More、proveの3つを覚えればテストを始めることができますので、
<続きの