Hi,

We've been using Test::Class at $WORK a few months now, and it's been very 
handy indeed. But there were a couple of things that were bothering me about 
it, and this afternoon I finally got around to having a look at them.

One of the annoyances is the apparently-random order that Test::Class runs 
classes in. This is not just bizarre, it caused me problems at one point with 
some of my classes which seemingly for no reason refused to work with 
Dancer::Test. I eventually narrowed it down to inheritance and the order in 
which objects were constructed - which, depending on what the *test class was 
called*, could mean that a child object was created either before or after its 
parent object.

The other annoyance is that, if you've got a whole bunch of test classes, and 
you've decided to run them all with Test::Class::Load, there's no way of 
telling what failed after your test script has finished, other than a useless 
list of test numbers which doesn't correspond to anything in my code. Setting 
the environment variable TEST_VERBOSE helps a bit, but it still involves 
grubbing around in the output of the tests (which unfortunately, because of the 
use of third-party modules I have no control of, is rather chatty).

I thought to myself "Test::Class is OO, so it should be easy to fix, even if I 
have to be naughty and sub-class private methods" - but no. All sorts of things 
that look like they should be methods are called as method($self, @params) 
rather than $self->method(@params) - I assume to avoid test classes overriding 
them accidentally.

Anyway, this is what I ended up with: 1) sorting the list of classes, so 
they're run in a more understandable order, and 2) tracking which classes 
failed, so we can tell the end user when all tests are done.

# Hack: if we're running multiple test classes, run them in alphabetical
# order, rather than the apparently-random order that Test::Class
# defaults to. And remember which classes failed so we can summarise
# failures when we're done testing.
my @classes_with_failures;
INIT {
    no warnings 'redefine';
    local *Test::Class::_test_classes = sub {
        my ($class) = shift;
        return (sort @{ mro::get_isarev($class) }, $class);
    };

    # Report if any tests failed in a module.
    my $orig_all_ok_from = \&Test::Class::_all_ok_from;
    local *Test::Class::_all_ok_from = sub {
        my $self = shift;
        my $all_ok = $orig_all_ok_from->($self, @_);
        if (!$all_ok) {
            print STDERR "# Tests failed in ", ref($self), "\n";
            push @classes_with_failures, ref($self);
        }
        return $all_ok;
    };
    use warnings 'redefine';

    # And run the test suite.
    Test::Class->runtests;
}

END {
    if (@classes_with_failures) {
        print STDERR "#\n# The following classes had test failures:\n#  ",
            join("\n#  ", @classes_with_failures), "\n#\n";
    }
}

Sam
-- 
Website: http://www.illuminated.co.uk/

Reply via email to