First off - has anyone done this?
Yup. One of the motivations behind Test::Class was solving exactly this sort of problem. Getting test running is pretty simple.
# create a base class that contains your generic tests
{ package BaseClass::Test;
use base qw( Test::Class );
use Test::More;
sub class_under_test {
my $self = shift;
my $class = ref $self;
$class =~ s/::Test$//;
return $class;
}
my $o;
sub create : Test( setup => 1 ) {
my $self = shift;
my $class = $self->class_under_test;
$o = $class->new;
isa_ok $o, $class;
}
sub answer : Test {
is $o->answer, 42;
}
# because we don't want our abstract test class to run
__PACKAGE__->SKIP_CLASS( 1 );
}
my @classes_to_test = qw( Foo Bar Ni );
# make a bunch of subclasses of our base test class
eval "package ${_}::Test; use base 'BaseClass::Test'"
foreach @classes_to_test;
# run everything
Test::Class->runtests;
__END__
# will output something like
1..6
ok 1 - The object isa Bar
ok 2 - answer
ok 3 - The object isa Ni
not ok 4 - answer
# Failed test 'answer'
# in foo.pl at line 44.
# got: 'infinity'
# expected: '42'
ok 5 - The object isa Foo
ok 6 - answer
# Looks like you failed 1 test of 6.
Where it currently falls down is the test reporting. It's currently a little tricky to see which particular subclass failed a test. You either have to add it in to the test description explicitly, or set TEST_VERBOSE - which is a little too verbose. Making this easier is on the to do list.