Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test::Most - report failed test with stacktrace

Tags:

perl

I'm fixing a large test script (> 1000 lines) that uses some utility methods (also > 1000 lines) to perform repeated tests on various initial data setups. This helps consolidate code. However, when a test fails it reports the line number of inside the utility method making it hard to trace which test failed.

Is it possible to configure Test::Most to give a stacktrace instead of just a single line number when a test fails?

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;

use Test::Most tests => 3;

ok(1, 'first test');

note "The following includes a failed test, but a stack trace would be more helpful";

helper_sub_with_test();       # Line 13

ok(1, 'third test');

sub helper_sub_with_test {
    ok(0, "second test");     # Line 17
}

Outputs:

$ perl scratch.pl 
1..3
ok 1 - first test
# The following includes a failed test, but a stack trace would be more helpful
not ok 2 - second test
#   Failed test 'second test'
#   at scratch.pl line 17.
ok 3 - third test
# Looks like you failed 1 test of 3.

As you can see, it would be helpful if the failed test reported both line 17 and line 13 for when there are multiple calls to the utility method.

like image 870
Miller Avatar asked Mar 17 '23 11:03

Miller


2 Answers

I don't believe that the Test::More infrastructure provides such a beastie, but do you really need a stack trace? Reporting line 13 alone should be sufficient, provided you give your tests descriptive names.

To report line 13 instead of line 17, just add the following to your sub:

local $Test::Builder::Level = $Test::Builder::Level + 1;

Longer example:

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;

use Test::Most tests => 3;

ok(1, 'first test');

note "The following includes a failed test, but a stack trace would be more helpful";

helper_sub_with_test();       # Line 13

ok(1, 'third test');

sub helper_sub_with_test {
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    ok(0, sprintf "second test (look at line %d)", __LINE__);     # Line 18
}
like image 184
tobyink Avatar answered Mar 23 '23 11:03

tobyink


The quick and dirty way to get what you want is to put a wrapper around Test::Builder::ok. This is how Test::Most operates.

Using Aspect makes this less of a hack.

use Carp qw(longmess);
use Test::Most;
use Aspect;

after {
    # For some reason, the return value is not being captured by Aspect
    my $last_test = ($_->self->summary)[-1];
    print longmess if !$last_test;
} call "Test::Builder::ok";

sub foo { ok(0) }

foo();
pass;

done_testing;
like image 44
Schwern Avatar answered Mar 23 '23 09:03

Schwern