I am currently working on a module with an really ugly testsuite (written by me of course ;) ). Refactoring a testsuite is no fun, but at least I want to remove all duplicated code. Currently, every single testfile looks like this:
#!perl -T
use strict;
use warnings;
BEGIN {
use lib 't';
use MyTestHelper;
use Test::More;
# check some external dependencies (programs and non
# CPAN modules
my ($skip,$msg) = MyTestHelper::skip_all();
plan skip_all => $msg if $skip;
}
our %features = ( A => 72,
B => 148,
C => 43,
D => 34, );
my $number_tests;
foreach ( keys %features ) {
$number_tests += $features{$_};
}
plan tests => $number_tests;
use English qw( -no_match_vars );
use Scalar::Util qw/refadr/;
use Data::Dumper;
use MyModule;
my $obj = MyModule->new();
FEATURE:
foreach my $feature ( sort keys %features ) {
SKIP: {
if ( $obj->does_not_like($feature)
{
skip "Feature not available", $features{$feature};
}
# FINALLY, the tests
...
}
}
Which is much code for boring stuff. I need this features feature because I test several back-ends (the features in the test). If a back-end is not available, we can skip all tests of this back-end. Another problem is that it only checks the absolute number of tests. So the test would pass if feature A had 73 tests and feature B 147 (which is not so far-fetched than you might think). I want a simple framework like:
package MyTestFramework;
use base 'Test::SimpleFramework';
sub init {
my ( $self ) = @_;
$self->set_strict(1);
$self->set_warnings(1);
$self->export({
'Scalar::Util' => 'refaddr',
'English' => '-no_match_vars',
'Data::Dumper' => undef,
'MyModule' => undef,
});
$self->dependencies({
'lftp binary in path' => $self->in_path('lftp'),
'non-CPAN module' => $self->in_inc('Non::Cpan'),
});
}
our @EXPORT = qw( delete_temporary_files );
sub delete_temporary_files {
foreach my $file ( <t/tmp/*> ) {
my ( $filename ) = $file =~ m{\A t/tmp/ ([\d\w\.\-]+) \z}xms;
unlink "t/tmp/$filename";
}
}
And now, my testfiles would look like this:
#perl -T
BEGIN{
use lib 't';
};
use MyTestFramework;
# skips all tests if one dependency was't found
check_dependencies();
register_features({
A => 72,
B => 148,
C => 43,
D => 34,
});
my $obj = MyModule->new();
while (my $feature = next_feature) {
skip_feature if $obj->does_not_like($feature->name);
# the tests
...
# dies if number of tests for feature is not correct
}
delete_temporary_files();
A little research on CPAN revealed, that the features of Test::SimpleFramework are a mixture of
Test::Class,
Test::Block (dying if number of tests of a feature is not correct) and
ToolSet, although my unexperienced eyes don't see a simple solution how these three modules could be combined. I am not sure whether the world needs yet another Test::* module, but how would you refactor my old code to get a similar functionality? If it is avoidable to introduce dependencies just for the testsuite, it would be a big plus.
Or would you like to see something like this on CPAN?
UPDATE: in my tests, "features" is "backends". "features" is probably the wrong generalization. What I actually do is to test different implementations of an interface API.