tokuhirom's Blog

Changing test results formatter with Test::Builder2(Currently beta)

http://d.hatena.ne.jp/gfx/20110810/1312933806
http://subtech.g.hatena.ne.jp/cho45/20110613/1307947167

Some of the people trying to change test results formatter with Test::Builder. But it's very painful and silly since Test::Builder doesn't support extending formatter.

Test::Builder2 supports to modify test results format. You can write your own formatter class.

use strict;
use warnings;
use utf8;

package Test::Builder2::Formatter::TAP::WithLineNumber;
use parent qw(Test::Builder2::Formatter::TAP::v13);

sub accept_result {
    my $self  = shift;
    my $result = shift;

    # FIXME: there is a lot more detail in the 
    # result object that I ought to do deal with.

    my $out = "";
    $out .= "not " if !$result->literal_pass;
    $out .= "ok";

    my $num = $result->test_number || $self->counter->increment;
    $out .= " ".$num if $self->use_numbers;

    # XXX HACKING BEGIN HERE
    my $line = sub {
        for (1..20) {
            my @caller = caller($_);
            return unless @caller;
            return $caller[2] unless $caller[0] =~ /^Test::/;
        }
    }->();
    my $name = "L$line: " . ($result->description || '');
    # XXX HACKING END HERE

    $self->_escape(\$name);
    $out .= " - $name" if defined $name and length $name;

    my $reason = $result->reason;
    $self->_escape(\$reason);

    my @directives;
    push @directives, "TODO" if $result->is_todo;
    push @directives, "SKIP" if $result->is_skip;

    $out .= " # @{[ join ' ', @directives ]} $reason" if @directives;
    $out .= "\n";

    $self->out($out);

    if(!$result->literal_pass and !$result->is_skip) {
        # XXX This should also emit structured diagnostics
        $self->_comment_diagnostics($result);
    }

    $self->seen_results(1);

    return;
}

1;

The code too long but most part is copy&pasted.

And you can use the formatter like following code:

use strict;
use warnings;
use utf8;
use lib './test-more/lib/';
use Test::More;

use Test::Builder2::Formatter::TAP::WithLineNumber;
Test::More->builder->event_coordinator->formatters(
    [ Test::Builder2::Formatter::TAP::WithLineNumber->new() ] );

ok 'heh' for 1..2;
fail 'heh' for 1..3;
ok 'heh' for 1..3;
fail 'heh' for 1..4;

done_testing;

And sample output is here:

TAP version 13
ok 1 - L11: 
ok 2 - L11: 
not ok 3 - L12: heh
#   Failed test 'heh'
#   at hoge.t line 12.
not ok 4 - L12: heh
#   Failed test 'heh'
#   at hoge.t line 12.
not ok 5 - L12: heh
#   Failed test 'heh'
#   at hoge.t line 12.
ok 6 - L13: 
ok 7 - L13: 
ok 8 - L13: 
not ok 9 - L14: heh
#   Failed test 'heh'
#   at hoge.t line 14.
not ok 10 - L14: heh
#   Failed test 'heh'
#   at hoge.t line 14.
not ok 11 - L14: heh
#   Failed test 'heh'
#   at hoge.t line 14.
not ok 12 - L14: heh
#   Failed test 'heh'
#   at hoge.t line 14.
1..12
# 7 tests of 12 failed.

Conclusion

  • You can write your own test results formatter with Test::Builder2
  • Test::Builder2 is currently β version.
  • I'm waiting to release Test::Builder2!