lukash
lukash

Reputation: 784

Perl tests - common parent for tests

I have a set of tests, always named Module.t, each one starts like this:

use 5.026;
use strict;
use warnings;

use Test::Perl::Critic (-severity => 3);
use Module::Path 'module_path';
use Test::More tests => 8;
use Test::Log4perl;
Test::Log4perl->suppress_logging;

BEGIN { use_ok("My::Module") }
critic_ok(module_path("My::Module"));

... actual tests for this module ...

It's done this way because a bunch of modules are not coded very nicely and in effort to refactor stuff as we go, I'm trying to write tests for individual modules over time. Eg. I can't just enable Perl::Critic for all sources cause it will blow up in my face.

I would like to ideally make a "parent" test for all of these so that when me or a different developer wants to write a new test they will always have all the required stuff. Something like:

use 5.026;
use strict;
use warnings;

# 6 tests because 2 (use_ok and critic_ok) are already in the parent
use parent ParentTest("My::Module", tests => 6);

... actual tests for this module ...

Does perl have a way of doing that?

Disclaimer: I'm a perl noob, so maybe this has a better solution :-)

Upvotes: 1

Views: 90

Answers (1)

melpomene
melpomene

Reputation: 85767

Sounds like you just want a helper module that loads some other modules and runs some initial tests for you.

Something like:

# ParentTest.pm
package ParentTest;
use strict;
use warnings;

use Test::Perl::Critic (-severity => 3);
use Module::Path 'module_path';
use Test::More;
use Test::Log4perl;

sub import {
    my (undef, $module, %args) = @_;

    $args{tests} += 2;

    plan %args;
    Test::Log4perl->suppress_logging;
    use_ok $module;
    critic_ok module_path $module;

    @_ = 'Test::More';
    goto +Test::More->can('import');
}

1

Usage would be:

use ParentTest "My::Module", tests => 6;

This is all untested, but the idea is:

  • We want to run some code to set up the initial test plan and run some tests.
  • We also want to export everything that Test::More exports, so our caller doesn't have to use Test::More themselves.
  • use Some::Module @args is equivalent to BEGIN { require "Some/Module.pm"; Some::Module->import(@args); }, so we can just put our custom logic in the import method.
  • We start by ignoring the first argument (which is a class name because import is called as a class method) and assigning the remaining arguments to $module and %args.
  • We increment $args{tests} by 2 to account for the two extra tests we perform automatically (if tests wasn't passed in, it is implicitly created here).
  • We pass %args to plan from Test::More, which is nice for setting up a test plan outside of the initial use line.
  • We perform the initial tests.
  • We tail call Test::More::import, erasing our own stack frame. This makes it look like our caller did Test::More->import(), which exports all the Test::More utility functions to them.
  • The unary + in goto +Test::More->... has no real effect, but it helps distinguish between the goto LABEL and goto EXPRESSION syntactic forms. We want the latter interpretation.

Upvotes: 4

Related Questions