http://qs1969.pair.com?node_id=612440

vbar has asked for the wisdom of the Perl Monks concerning the following question:

I've written a Perl module which includes a Perl script to exercise the packaged functionality. The module has unit tests, some of which test the script: execute it via the "system" call and compare outputs to pre-recorded data. On my Linux machine, all the tests pass, so I uploaded the module to CPAN.

Unfortunately, when CPAN tests my module some more, its unit test often fail - it looks like they don't work anywhere but on linux, and while the specific failure reports aren't exactly the same, all agree that it's the script which won't run. So, what should I do with it? I can't take the script out - it's really necessary for the module to be useful. I suppose I don't have to test it, but that's a cop-out... Surely lots of people are including scripts with their Perl modules - is there some special way to package them?

  • Comment on How to unit test a script packaged in a module distribution?

Replies are listed 'Best First'.
Re: How to unit test a script packaged in a module distribution?
by xdg (Monsignor) on Apr 27, 2007 at 21:15 UTC
    execute it via the "system" call

    It's easy to do this non-portably. For example, if you rely on the script being executable and having a shebang line, this won't work on Windows unless .pl file associations have been set up.

    I've done a fair amount of script testing and always set up an explicit call to perl, usually with Probe::Perl and relying on IPC::Run3 to execute the program and manage input and output.

    For example:

    use strict; use File::Spec; use IPC::Run3 qw/run3/; use Probe::Perl; use Test::More; plan tests => 2 ; my $perl = Probe::Perl->find_perl_interpreter; my $script = File::Spec->catfile(qw/scripts foo.pl/); my ($got_stdout, $got_stderr); ok( -r $script, "foo.pl script readable" ); # run and capture output run3 "$perl $script", undef, \$got_stdout, \$got_stderr; is( $got_stdout, $expected_stdout), "foo.pl program output" );

    You can see an example like that in my Tee distribution in 02_tee_script.t.

    In some cases, I've written an entire test helper class to manage repetitive tests on a script. In Pod::WikiDoc, I have a file called t/CLI.pm and I use it for repeated calls to a script with different command line options, plus it automatically captures output and has tests for whether it ran successfully or not.

    use t::CLI; my $script = File::Spec->catfile( "scripts", "wikidoc" ); my $wikidoc = t::CLI->new($script); # setup $input_file and $output_file $wikidoc->runs_ok( "$input_file", "$output_file" ); $wikidoc->stdout_like( qr/Extracting Pod from \Q$input_file\E/, "'wikidoc file file' status message" );

    Here's is a copy of t::CLI:

    You can see that in use in t/70-wikidoc-script.t.

    I hope that gives you some useful ideas.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Yes, I'm definitely going to give IPC::Run3 and Probe::Perl a try - thanks a lot! The tests work on my machine now - we'll see how CPAN handles them...
Re: How to unit test a script packaged in a module distribution?
by samtregar (Abbot) on Apr 27, 2007 at 19:21 UTC
    You could abstract out the code in the script into a module with a single method call - i.e. run(). Then you could write tests which setup @ARGV and call run() to simulate running the script.

    Alternately, you could make the script tests skip when they're running on Windows, but perhaps there are other OSes that can't easily execute scripts.

    -sam

      Yes, I can assure you there's many OSes out there not executing my script :-/ - one thing I forgot was that a system can have more than one Perl interpreter...

      As for abstracting out the code, I guess it's possible, if I also changed all my print statements to accumulate the output instead, but I think it would be warping it too much. The module's experimental, after all - I need to think about what it should be doing before testing that it does...

Re: How to unit test a script packaged in a module distribution?
by eyepopslikeamosquito (Archbishop) on Apr 28, 2007 at 06:51 UTC

    Chapter 9 of the excellent book Perl Testing: A Developer's Notebook contains an interesting tactical trick to better structure scripts for testability. Their idea is to have a one-line script mainline that calls a main() function; that way you can test the script more easily from outside by hooking into the script's main() function. If you are seriously interested in Perl testing, I strongly recommend getting this book.