Sam Kington
2012-06-07 15:13:16 UTC
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
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/
Website: http://www.illuminated.co.uk/