tags:

views:

114

answers:

2

I am getting started with Test::More, already have a few .t test scripts. Now I'd like to define a function that will only be used for the tests, but across different .t files. Where's the best place to put such a function? Define another .t without any tests and require it where needed? (As a sidenote I use the module structure created by Module::Starter)

+9  A: 

The best approach is to put your test functions, like any other set of functions, into a module. You can then use Test::Builder to have your test diagnostics/fail messages act as if the failure originated from the .t file, rather than your module.

Here is a simple example.

package Test::YourModule;

use Test::Builder;
use Sub::Exporter -setup => { exports => ['exitcode_ok'] };  # or 'use Exporter' etc.

my $Test = Test::Builder->new;

# Runs the command and makes sure its exit code is $expected_code. Contrived!
sub exitcode_ok {
    my ($command, $expected_code, $name) = @_;

    system($command);
    my $exit    = $? >> 8;
    my $message = $!;

    my $ok = $Test->is_num( $exit, $expected_code, $name );
    if ( !$ok ) {
        $Test->diag("$command exited incorrectly with the error '$message'");
    }

    return $ok;
}

In your script:

use Test::More plan => 1;
use Test::YourModule qw(exitcode_ok);
exitcode_ok('date', 0, 'date exits without errors');
rjh
I agree with all of this, but I would generally put test-specific functionality in a module in the t/ directory, so it cannot be confused with the actual application -- e.g. in MyApp/t/Common.pm, with the package name MyApp::t::Common.
Ether
I would recommend Test::Builder::Module as it will take care of writing a smart import() for you, one that can handle a test plan. This allows your module to stand alone from Test::More.
Schwern
This is a bit heavy handed when the common code you want to share isn't testing anything. Utility functions and various other things that aren't going to emit TAP won't benefit from a Test::Builder wrapper.
brian d foy
+6  A: 

Write a module as rjh has demonstrated. Put it in t/lib/Test/YourThing.pm, then it can be loaded as:

use lib 't/lib';
use Test::YourThing;

Or you can put it straight in t/Test/YourThing.pm, call it package t::Test::YourThing and load it as:

use t::Test::YourThing;

The upside is not having to write the use lib line in every test file, and clearly identifying it as a local test module. The down side is cluttering up t/, it won't work if "." is not in @INC (for example, if you run your tests in taint mode, but it can be worked around with use lib ".") and if you decide to move the .pm file out of your project you have to rewrite all the uses. Your choice.

Schwern
Okay for the moment I'll go with this one even If I'll have a look at Test::Builder in a moment of time.
zedoo