Perl Hackers Hub

第36回Perlのテストモジュールの使い方・作り方-Test::More、Test::Builder、Test::Stream、そしてPerl 6(3)

(1)こちら⁠2)こちらから。

Test::Stream─⁠─新しいテストフレームワーク

Test::Builderはほぼすべてのテストモジュールのベースとなっているフレームワークですが、1つのモジュールにすべての機能(テスト数のカウントやテスト結果のフォーマットなど)が詰め込まれたモノシリック構造になっていることや、機能拡張用のAPIフックポイントが用意されていないことなど、拡張性はあまり考慮されていません。そのため、Test::Builder自体の機能を拡張したい場合はそれぞれのテストモジュールがTest::Builderのメソッドにパッチを当てていくしかありませんでした。

Test::Builder2の開発スタート、そして中止

このような状況を受けて、2008年ごろにTest::MoreのメンテナーだったMichael G SchwernによってTest::Builderを根本的に作りなおすTest::Builder2の開発がスタートしました。これはモノシリックな構造だったTest::Builderを全面的に書き換え、APIを整備し、拡張しやすい構造に作り替えるプロジェクトでしたが、結局正式にリリースされることはなく、長い開発期間を経て2014年に開発は中止されました。

Test::Streamの開発スタート

Test::Builder2の開発中止が決まったタイミングでTest::Moreのメンテナーを引き継いだChad Granumが開発を進めている新しいテストフレームワークがTest::Streamです。

Test::Streamの特徴は次のとおりです。

  • islikeがリファレンスもテスト可能に
  • TAP出力のUTF-8サポート
  • プラグイン機構の導入
  • テスト結果のオブジェクト化

isがリファレンスもテストできるようになったので、isis_deeplyを使い分ける必要がなくなりました。比較したいデータを渡せばTest::Stream側でよしなに対象のデータ型を判断してくれます。また、TAPの出力でUTF-8がサポートされたので、Unicodeを含む文字列があっても、警告メッセージが出力されることはありません。

use Test::Stream -V1;
is( +{ lang => 'perl5' }, +{ lang => 'perl6' }, '違う言語?';
done_testing;

テストが失敗したときのメッセージもとてもわかりやすくなりました。先ほどのテストを実行すると次のように表示されます。

not ok 1 - 違う言語?
# Failed test '違う言語?'
# at stream.t line 2.
# +--------+-------+----+-------+
# | PATH   | GOT   | OP | CHECK |
# +--------+-------+----+-------+
# | {lang} | perl5 | eq | perl6 |
# +--------+-------+----+-------+
1..1

プラグイン機構の導入とともに、従来のTest::More相当の機能はもちろんのこと、Test::ExceptionTest::Outputといったメジャーなテストモジュール相当の機能がプラグインとして提供されていますし、さらにはBDDBehavior Driven Development振舞駆動開発)スタイルでテストを書くためのプラグインまで用意されています。

テストモジュールを作るうえでは、テスト結果が最初からオブジェクト化されているため、interceptという関数で簡単にテスト結果をテストできるようになりましたリスト5⁠。

リスト5 test_stream.t
use Test::Stream -V1, -Tester;
my $results = intercept {
    is(1, 1, 'success test');
    is(1, 2, 'failure test');
};
is($results->[0]->name, "success test");
like(
    $results->[1]->diag,
    [qr/Failed test 'failure test'/],
);
done_testing;

Test::StreamはCPANにアップされているのでcpanm Test::Streamでインストールできます。執筆時点(2015年11月)では一部の機能は実験リリース扱いとなっていますが、今後のPerlのテスト環境を大きく改善する注目のプロジェクトです[3]⁠。

Perl 6のテスト

最後にPerl 6のテストについて少し紹介します。

2015年のPerl界隈の最大のニュースと言えば、Perl 6がついに正式リリースされると発表されたことです。執筆時点の最新テストリリースであるRakudo Star 2015.09をベースに、Perl 6のテスト方法を紹介します[4]⁠。

Testモジュール

Perl 6にはそのものずばりTestというテストモジュールが用意されていて、ほぼTest::MoreTest::Exception相当のテスト関数が用意されています。テストのスタイルも従来のPerl(以降、区別するためにPerl 5と記載します)と同じですリスト6⁠。

リスト6 perl6-test.t
use v6;
use Test;
is("Perl6".chars, 5, "文字列の長さのテスト"); ―(1)
my $str = "Perl 6 will be available this Christmas";
like($str, rx:i/christmas/, "正規表現のテスト"); ―(2)
subtest { ―(3)
    # Perl 6のオブジェクト機構を使ってクラスを定義
    class Person {
        has Str $.name is rw;
        has Int $.age is rw;
    }
    # オブジェクトの作成と、プロパティの設定
    my $person = Person.new;
    $person.name = "Larry";
    $person.age = 61;
    # プロパティのテスト
    is( $person.name, 'Larry', 'name属性のテスト');
    cmp-ok($person.age, &infix:«>=», 60, 'age属性のテスト'); ―(4)
}, 'オブジェクトのテスト';
throws-like( ―(5)
    sub { die "throw exception" },
    X::AdHoc, # dieの送出する例外の型は、X::AdHoc
    message => "throw exception",
    "例外のテスト"
);
done-testing; ―(6)

Perl 5のテストに慣れている人ならば、リスト6を見ただけでだいたい雰囲気はつかめると思いますが、それぞれの意味は次のとおりです。

  • (1)(2)islikeは、Test::Moreの同名のテスト関数と同じ
  • (3)subtestは、引数の順番が変わった以外はTest::Moresubtestと同じ
  • (4)cmp-okは、関数名が(Perl 6の命名付与規約に合わせて)変わったのと、比較演算子によって多少指定のしかたが変わった以外はTest::Morecmp_okと同じ
  • (5)throws-likeは、Perl 6で導入された例外クラスをテストするテスト関数
  • (6)done-testingは、Test::Moredone_testingと同じ

また、リスト6ではテスト名をすべて日本語で書いていますが、Perl 6では文字列のデフォルトのエンコ ーディングがUTF-8になったので、そのまま出力しても警告メッセージは出力されません。

テストの実行と結果の確認

テストを実行すると結果がTAP形式で出力される点は、Perl 5と完全に同じです。

Perl 6のテストの実行結果
$ perl6  perl6-test.t
ok 1 - 文字列の長さのテスト
ok 2 - 正規表現のテスト
    ok 1 - name属性のテスト
    ok 2 - age属性のテスト
    1..2
ok 3 - オブジェクトのテスト
    1..3
    ok 1 - code dies
    ok 2 - right exception type (X::AdHoc)
    ok 3 - .message matches throw exception
ok 4 - 例外のテスト
1..4

proveコマンドは(現時点では)Perl 5のproveコマンドをそのまま利用します。Perl 6のテストも拡張子は.tを使用するので、Perl 5と区別するために--exec perl6オプションを付けて実行します。

Perl 6におけるproveコマンドの実行
$ prove --exec perl6 perl6-test.t
perl6-test.t .. ok
All tests successful.
...
Result: PASS

Perl 6の仕様をテストするroast

Perl 5では「実装が仕様」だったので明確な仕様書は存在しませんでしたが、Perl 6には明確に仕様書が存在します

さらに、この仕様書どおりに実装されていることを確認するためのroastというテストセットも用意されています。このテストセットでも当然Testモジュールが使われています。Perl 6を学習するためには、まずこのテストを一通り見ることをお勧めします。

まとめ

Perlのテストモジュールの使い方と作り方を一通り解説し、最後に少しだけPerl 6でのテスト方法を紹介しました。

Perlのテストは、Test::More、TAPの読み方、proveコマンドさえ覚えればすぐに始めることができます。足りない機能があればCPANモジュールを使ったり、Test::Builderで新しいテストモジュールを作ったりすることで対応できます。読者のみなさんも、今回の記事を参考に、ぜひどんどんテストを書いてみてください。Happy Perl Testing!

さて、次号の執筆者はpapixさんで、テーマは「Perlで作るInfrastructure as Code」です。

おすすめ記事

記事・ニュース一覧