Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

schwern (1528)

schwern
  (email not shown publicly)
http://schwern.net/
AOL IM: MichaelSchwern (Add Buddy, Send Message)
Jabber: schwern@gmail.com

Schwern can destroy CPAN at his whim.

Journal of schwern (1528)

Wednesday April 23, 2008
06:59 PM

Embedding tests in a script

[ #36235 ]

From time to time I'll work on a code base that's basically a pile of individual scripts. The process of converting it to a modularized system can take some time, technically as well as socially. Meanwhile, I have to get work done. And for me getting work done requires writing tests.

But if it's a pile of scripts, where do you put them? And with no build structure, how do you run them? Rather than having to decide between using a single file OR writing tests, I decided to embed the tests in the scripts themselves. Observe.

sub selftest {
    my @test_functions = get_test_functions();
 
    for my $function (sort { lc $a cmp lc $b } @test_functions) {
        no strict 'refs';
        print "# Running $function\n";
        &{$function};
    }
}
 
sub get_test_functions {
    my $package = shift || __PACKAGE__;
 
    # Load the test functions after __END__
    eval join '', <DATA>;
 
    no strict 'refs';
 
    return
      # Select only those which are subroutines
      grep { defined &{$_} }
 
      # Find the ones named test_*
      grep /^test_/,
 
      # Get all the symbols in the package
      keys %{$package."::"};
}
 
use Getopt::Long;
 
sub main {
    my %options;
    GetOptions(
        \%options,
        "test",
    );
 
    if( $options{test} ) {
        selftest();
        exit;
    }
 
    ... rest of the code here ...
}
 
main();
 
__END__
# These tests will be compiled and run when --test is given
 
use strict;
use warnings;
 
use Test::More 'no_plan';
 
sub test_the_tests {
    pass("The tests run!");
}

Giving a --test compiles the __END__ code (in selftest()), finds all the test_* functions, runs them and exits.

By embedding the tests into the scripts you can introduce unit testing to single-file scripters without having to simultaneously introduce the concept of a multi-file project. By putting the tests after the __END__ block nobody can make the excuse that your test functions are wasting memory in production.

I'm sure I'm not the first to come up with this, but I don't know that I've seen it modularized. So before I go and do that, is this already on CPAN?

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
    • What an amazing coincidence [perl.org]. I still need to get POD example testing back into Test::Inline 2.

      Test::Inline and selftest are similar in that they both follow the idea of embedding the tests into the code being tested. Test::Inline focuses on putting the test as close to the code being tested as possible, but it requires scaffolding to run those tests. selftest focuses on being able to deliver a single, self-contained package.

      I think I'll release it as Test::Yourself. What do you think?
    • Test::Inline (both Schwern's original Pod::Tests and my replacement) aren't really suitable for this situation, as he's applying it to standalone scripts rather than distributions.