Test::Class::MOP now has an 'is testcase' trait
When I previously blogged about Test::Class::MOP, I showed the bare basics of a test. Per a suggestion from Toby Inkster, I've now added an is testcase
trait for methods. Unlike Test::Class::Moose, test methods no longer need to start with test_
. Instead, you can do something like this:
method simple_test($report) is testcase {
$report->plan(1);
is $self->test_fixture->full_name, 'Bob Dobbs',
'Our full name should be correct';
}
Obviously there's a lot going on there, so I'll explain a bit more about this.
Let's say we have a very simple Moose class (I'm using a Moose class for the code to make it clear that Test::Class::MOP
is not just for testing MOP code):
package Person;
use Moose;
has [qw/first_name last_name/] => ( is => 'ro' );
sub full_name {
my $self = shift;
return join ' ' => $self->first_name, $self->last_name;
}
And here's a moderately advanced test for it:
use mop;
class TestsFor::Person
extends Test::Class::MOP
with Test::Class::MOP::Role::AutoUse {
use Test::Most;
has $!test_fixture is rw;
# XXX bare return required due to this bug:
# https://github.com/stevan/p5-mop-redux/issues/148
method extra_constructor_args { return }
method test_setup($report) {
$self->next::method($report);
$self->test_fixture($self->class_name->new(
first_name => 'Bob',
last_name => 'Dobbs',
$self->extra_constructor_args,
));
}
method simple_test($report) is testcase {
$report->plan(1);
is $self->test_fixture->full_name, 'Bob Dobbs',
'Our full name should be correct';
}
}
The class
declares our test class name. The extends
says that this class inherits from Test::Class::MOP
(side note: p5-mop is single inheritance only. Use roles or delegation if you want to share behavior). The with Test::Class::MOP::Role::AutoUse
says "strip the prefix from the class name and use
the resulting package. For those who prefer less magic, it's simple:
class TestsFor::Person extends Test::Class::MOP {
use Person ...
Everything else should be fairly clear. That seems like an awful lot for a single test, but those who are used to Test::Class
(or xUnit testing in general) know how well this quickly scales up to handle large test suites.
And subclassing that test (assumes we have a Person::Employee
class):
use mop;
class TestsFor::Person::Employee extends TestsFor::Person {
use Test::Most;
method extra_constructor_args {
return ( employee_number => 666 );
}
method simple_test($report) is testcase {
$self->next::method($report);
$report->plan(1);
is $self->test_fixture->employee_number, 666,
'... and we should get the correct employee number';
}
}
I would like to eventually extend the is testcase
trait to allow this:
method order_items_consistent($report) is testcase( tests => 7 ) {
...
}
But so far, I haven't figure out how to do that and get the plan back to the method level. I think keeping this simple at first is a better way to go.
Adding the is testcase
trait was done via a custom metaclass I wrote and then implemented with:
class Test::Class::MOP meta TestClassMeta {
...
}
The metaclass itself is clumsy because I was having some issues with namespaces, but since it's sufficiently encapsulated, I hope to fix that in the the future. In fact, if I can figure out how to make the optional plans work, I may just dump the $report
argument entirely. That should make things even cleaner.
Test::Class::MOP
is should hopefully soon be integrated into p5-mop-redux
's Travis CI setup, letting me know quickly if changes to the mop break my code.
Leave a comment