Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Perl Code Kata: Testing Imports
by chromatic | Pages: 1, 2

Tips, Tricks, and Suggestions

If you've worked your way through writing tests for the three examples, here are the approaches I would take. They're not the only ways to test these examples, but they do work. First, here is some background information on what's happening.

Reloading

To test import() properly, you must understand its implications. When Perl encounters a use module; statement, it executes a two-step process immediately:

BEGIN
{
    require module;
    module->import();
}

You can subvert both of these processes. To force Perl to reload a module, you can delete its entry from %INC. Note that all of the keys of this special hash represent pathnames in Unix format. For example, even if you use Windows or VMS or Mac OS 9 or earlier, loading Filter::Simple successfully should result in %INC containing a true value for the key of Filter/Simple.pm. (You may also want to use the delete_package() function of the Symbol module to clear out the namespace, though beware of the caveats there.) Now you can require the module again.

Re-importing

Next, you'll have to call import() manually. It's a normal class method call, however, so you can provide all of the arguments as you would to a function or method call.

You can also switch packages, though make sure that you qualify any calls to Test::* module functions appropriately:

package Some::Other::Package;

module->import( @args );

main::ok( 1, 'some test label' );

# or 

::ok( 1, 'some test label' );

Testing Exports

There are at least two techniques for checking the import of functions. One is the use of the defined keyword and the other is through the can() class method. For example, tests for Example #1 might be:

use_ok( 'Basic::Exports' );
ok( defined &foo,              'module should export foo()' )
ok( __PACKAGE__->can( 'bar' ), '... and should export bar()' );

To test that these are the right functions, call them as normal and check their return values.

By the way, the presence of the __PACKAGE__ symbol there allows this test to take place in other namespaces. If you haven't imported the ok() test function into this namespace, remember to qualify it, import it manually, or alias it so that the test program will itself run. (It may fail, which is fine, but errors in your tests are difficult and embarrassing to fix.)

Testing Non-Exports

It's difficult to prove a negative conclusively, but if you reverse the condition of a test, you can have good confidence that the module hasn't provided anything unwanted.

use_ok( 'Optional::Exports' );
ok( ! __PACKAGE__->can( 'foo' ),
    'module should not export foo() by default' );

The only tricky part of the tests here is in trying to import functions again. Call import() explicitly as a class method of the module. Switching packages within the test can make this easier; you don't have to unload the module if you do this.

Testing Weird Exports

The easist way to test an import() function that relies on command-line invocation or produces weird side effects that you may not want to handle in your current program is to launch it as a separate program. There are plenty of options for this, from system to fork and exec to tricks with pipes and shell redirection. IPC::Open3 is one good approach, if you want to use it in your test suite:

#! perl

use strict;
use warnings;

use blib;
use IPC::Open3;

use Test::More tests => 3;

use_ok( 'Export::Weird' );

my $pid = open3(
    undef, my $reader, undef,
    $^X, '-Mblib', '-MExport::Weird', '-e', '1'
);

my @out = <$reader>;
is( @out,                                1,
    'cli invocation should print one line' );
is( $out[0], "Invoked from command-line\n",
    '... with the right message' );

$^X represents the path to the Perl binary currently executing this program. The -Mblib switch loads the blib module to set @INC in the program appropriately. Depending on how you've set up your directories and invoke this program, you may have to change this. The other commands follow the invocation scheme given in Example #3.

Conclusion

You should now have several ideas on how to test import() methods of various kinds. For more details, read the tests of Pod::ToDemo or Test::Builder, which play strange games to achieve good test coverage.

If you've found a differently workable approach, I'd like to hear from you. Also, if you have suggestions for another kata (or would like to write one), please let me know.