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.
|
Related Reading Learning Perl Objects, References, and Modules |
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 1 of 1.
- My Approach
2004-12-31 00:56:15 shlomif [Reply]
Well, I used the "PACKAGE::" meta-hash to determine for the existence of symbols:
ok(exists(${Hello::}{'foo'}),
"Checking for existence of foo");
And I simply called it using ->().
I did not trust the fact that I could delete a file from %INC, etc, and simply wrote one test for every use statement. I also used "use" instead of calling import() explicitly.
For the Optional-Exports exercise I wrote a script that generated the tests for all the combinations.
For the load-time behaviour test, I made an assumption (which I retrospectively found to be wrong) that you don't want me to invoke the script in a separate way, and so used the "#line 0" directive to fake the line number, (thanks to integral from FreeNode for pointing me to it), to fake it and some pipe() and redirection games to trap STDOUT.




