Major Test::Class::Moose Update

Test::Class::Moose version 0.40 has been uploaded to the CPAN. Or you can get it now on github. It's a major improvement in many ways, including the fact that parallel testing is now in main branch and it works much better! Read on for the changes.

The first change is deprecating the annoying second argument to each test method. Instead of this:

sub test_froblinator {
    my ( $test, $report ) = @_;
    $report->plan(7);
    ...
}

You write this:

sub test_froblinator {
    my $test = shift;
    $test->test_report->plan(7);
    ...
}

The second argument is still available for backwards-compatibility, but it will be removed after a long deprecation cycle.

The reason I deprecated it is because you rarely need the report object (outside of the test setup) and I kept forgetting to pass it around.

Next, the test report object now has a current_method() method. That returns either the test method you are in or the test method you're about to run (if you're in a setup method). It does not work in test_startup because we don't yet know what the upcoming test method is. Why is this useful? Well, you can do things like this:

sub test_setup {
    my $test = shift;
    $self->next::method;
    my $report= $test->test_report;
    my $method = $test->current_method; # the test method we're about to run
    if ( $method->name =~ /customer/ ) {
        $test->load_customer_fixture;
    }
}

In other words, only load fixtures if you need them! Better still, if you have Sub::Attribute installed, you can do this instead:

sub test_setup {
    my $test = shift;
    $self->next::method;
    my $report= $test->test_report;
    my $method = $test->current_method; # the test method we're about to run
    if ( $method->has_tag('db') ) {
        $test->load_database_fixture;
    }
}

Imagine that you have ten test methods in a class but only three of them need the database. You can use your test_setup method to only load database fixtures if you need them. That's a major win!

Also, with parallel testing (this again requires Sub::Attribute), I've made all tests tagged with noparallel be automatically added to the sequential tests run after the other test methods are run in parallel.

And as an added, experimental, and undocumented feature, you can now do this to run an individual test class:

prove -lv t/tests/TestsFor/Some/Test/Class.pm

It's undocumented because the code for that is a bit of an ugly hack.

If you're curious, here's the driver t/tcm.t class I'm using for one test suite:

use strict;
use warnings;
use Test::Class::Moose::Load 't/tests';

use Getopt::Long;
GetOptions(
    'jobs=i'        => \my $jobs,
    'statistics=i'  => \( my $statistics = 1 ),
    'show_timing=i' => \my $show_timing,
) or die;

$jobs = 1 if ( $jobs || 0 ) < 2;

@ARGV = get_modules(@ARGV);

# use your base class name here, NOT Test::Class::Moose. Otherwise, the roles
# you apply won't be seen.
TestsFor::MyApp->new(
    statistics   => $statistics,
    show_timing  => $show_timing,
    jobs         => $jobs,
    test_classes => \@ARGV,
)->runtests;

sub get_modules {

    # the '::' fixes an issue where we might have done this:
    #     prove -l t/tcm.t :: Some::Test::Class
    # but realize that we needed the debugger and did this instead:
    #     perl -Ilib -d t/tcm.t :: Some::Test::Class
    return map { filename_to_package($_) } grep { $_ ne '::' } @_;
}

sub filename_to_package {

    # not portable, but currently only running on *nix type systems.
    my $thing = shift;
    return $thing unless $thing =~ m[t/tests];
    $thing =~ s[(?:.*)?t/tests/][];
    $thing =~ s[\.pm$][];
    $thing =~ s[/][::]g;
    return $thing;
}

__END__

=head1 NAME

t/tcm.t - Run MyApp tests

=head1 DESCRIPTION

This is the test class Moose driver for MyApp. You can run it with:

 prove -l t/tcm.t                                       # run all tests
 prove -l t/tcm.t :: -j 4                               # run all tests in four jobs
 prove -l t/tcm.t :: TestsFor::MyApp::Controller::Admin # run one test class
 prove -l t/tcm.t :: t/tests/TestsFor/MyApp/Controller/Admin.pm # run one test class

Again, using a single driver is a huge win. Separate .t tests take about 75 seconds to run on my main box (185 seconds on my laptop), and I can get them down to about 10 seconds with -j 4 and a single driver script (13 seconds with only one job, but as the test suite grows, that performance improvement will increase over time).

Enjoy!

Update: Version 0.40 would fail one test if you didn't have Sub::Attribute installed. Version 0.41 is on its way to the CPAN now.

Leave a comment

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/