Magnolia Tech

いつもコードのことばかり考えている人のために。

Test2::Prettyを作るために調べたことと、やったこと

Perl 5.26.xでTest2がコアモジュール化しました

Perl 5.26.xでは、コアモジュールのテスティングフレームワークであるTest::Moreモジュールに大改修が行われ、完全にゼロから書き直されれたTest2モジュールベースのものに入れ替えられています(version 1.3xx以降がTest2ベースです)。

Test2そのものの機能説明や、コアモジュール化の状況は、YAPC::Fukuokaでakiymさんが発表された「新時代のテストフレームワークTest2」というスライドをご参照ください。

akiym.hateblo.jp

Test2では、それまでのTAP(Test Anything Protocol)べったりの内部構造から、テストを1つの「イベント」として捉え、テストイベントのストリームを処理する、という思想に変わっています(事実、初期はTest::Streamという名前で開発が始められていました)。おなじみのTAP形式での出力も、単なるフォーマッタによる1つの表現形式に過ぎません。

コアモジュール化により失ったもの

しかし、一方でTest::Moreに対するモンキーパッチで作られていた特殊なテストモジュールが動作しなくなる、という弊害も有ります。執念のような互換性テストにより、たいていのメジャーなテストモジュールはTest2上で動作しますが、根本的にどうしようもないものも有ります。

その中でも最も有名なのが、tokuhiromさん作のTest::Prettyです。

$ cpanm Test::Pretty
--> Working on Test::Pretty
Fetching http://www.cpan.org/authors/id/T/TO/TOKUHIROM/Test-Pretty-0.32.tar.gz ... OK
Configuring Test-Pretty-0.32 ... OK
Building and testing Test-Pretty-0.32 ... FAIL
! Installing Test::Pretty failed. See /xxxx/xxxxxx/.cpanm/work/1521365294.14600/build.log for details. Retry with --force to force install it.

残念ながらテストが通らずインストールが失敗します。これはTest::MoreのコアであるTest::Builderモジュールを徹底的に置き換えるというTest::Prettyの構造がさすがにTest2ベースのTest::Builderでは対応できなかったためです。

Test2::Formatterモジュール

Test2にはTest2::Formatterという、発生したテストイベントを実際のテスト結果の出力へ変換する仕組みが用意されています。例えば、TAP形式への変換はTest2::Formatter::TAPというモジュールが用意されています。

というわけで、Test::Prettyの出力を再現するTest2::Formatter::Prettyを作ってあげれば良い、ということですね。

まずは最小のTest2::Formatter::Prettyを用意してみましょう。Test::Formatterを継承したモジュールは最低限これだけ有れば動きます。

package Test2::Formatter::Pretty;

use strict;
use warnings;

our $VERSION = 'v0.0.1';

use Test2::Util::HashBase qw{
    no_numbers
};

use parent qw/Test2::Formatter/;

sub hide_buffered { 1 }

sub write {
    my ($self, $e, $num, $f) = @_;

    print "にゃーん\n";
}

1;
use Test::More;

use strict;
use warnings;

pass("success!!");
fail("failure!!");

done_testing;
$ T2_FORMATTER='Pretty' perl -Ilib test01.t
にゃーん
にゃーん
にゃーん
にゃーん
にゃーん
$ perl test01.t
ok 1 - success!!
not ok 2 - failure!!
#   Failed test 'failure!!'
#   at test01.t line 7.
1..2
# Looks like you failed 1 test of 2.

テストが2つ(成功と失敗)、ダイアログメッセージが2つ(テストの失敗の詳細と、テストの失敗数)、テストプラン(1..2)が出力されていることが分かるでしょう。

つまり、writeメソッドの中でテストイベントに応じて出力する内容をひたすら変換し、標準出力か、標準エラー出力へ編集していけばOKということになります。

Test::Prettyの仕様を振り返る

ではここで改めてTest::Prettyの仕様を振り返ってみます。

  • テストが成功すれば「ok」ではなく、「✓」を緑で表示する -> 見やすい
  • テストが失敗すれば「not ok」ではなく、「✖」を赤で表示する -> 見やすい
  • テストをスキップすれば、「skip」を黄色で表示する -> 見やすい
  • サブテスト自体のテスト成否を表示しない -> サブテストの中身を見れば充分なので、表示が余計
  • テスト名を宣言しないと、テストコードの該当行のソースをテスト名として表示する -> 実は意外とこれで充分

環境変数HARNESS_ACTIVEが有効なときの挙動はまた違うんですが、複雑になるので、ここでは割愛します。

Test2::Formatter::Prettyを実装する

というわけで、できました「Test2::Pretty」モジュールです。

github.com

はっきり言ってTest2::Formatter::TAPと、Test::Prettyからのコピペのキメラなので、詳しくはソースコードを見てください、としか言い様はありませんが、Test2モジュールがイベントモデルとして綺麗に整理されているので、実装量は非常に少ないです。一部サブテスト周りで表示をスキップするために汚いコードが有りますが、順次改善していきたいと思いますので、使ってみてどんどん感想をください。issue、PRも歓迎です。落ち着いたら、cpanにアップします。

おわりに

長年ウォッチしていたTest2がついにコアモジュール化して、感慨深いものが有ります。 ただ、最近すっかり追いかけていなくて、今回久しぶりにコードを読んでみると全然別物のようになっていたので、追いかけるのに非常に苦労したし、意外とドキュメントも未整備なところが有ったので、これからまたコントリビュートしたい、と思いました。

Test2::Pretty、ぜひ使ってみて下さい。

初めてのPerl 第7版

初めてのPerl 第7版

続・初めてのPerl 改訂第2版

続・初めてのPerl 改訂第2版