Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

RFC: Runnable test code integrated into Modules.

by BrowserUk (Pope)
on Jan 30, 2003 at 11:06 UTC ( #231256=perlmeditation: print w/replies, xml ) Need Help??

I've recently been trying to put together a module for distribution and it struck me that in the same way that using POD keeps the documentation together with the module, it would be useful if you could do the same thing with a test suite/program. It would also be useful if the test program was useable directly for install time verification.

I thought about various schemes of having a test function or method that could be invoked, which would be okay, but still require another script--even if it is only a -e one-liner to invoke the test code.

Then inspiration hit me and I remembered 2 things I had seen recently.

  1. A comment in the CB that a module doesn't need a shebang line as it isn't intended for direct extecution.
  2. -x command line switch that can be used to skip over junk at the top of a script until a shebang line is found and start executing there.

Putting the two together took a little experimentation, but the result is that you can construct a .pm file that acts in every way that I have tried as a normal module when invoked via either a use my::module; statement or a require 'my::module'; or even a one-liner using the -m switch.

However, if the module is run as a program using the -x switch, it will execute the embedded program, load itself as a module and function as a 'normal' script would.

A trivial example

package My::Module; sub new{ bless \rand, shift(); } sub do_something{ print 'Do what?'; } return 1; =head1 NAME My::Module - Test my idea. =head1 Synopsys/Example See demo/test code at the end of this module. =head1 Description Demo of idea to integrate a runnable test suite into a module =head1 Copyright Copyright 2003, by BrowserUK. All rights reserved. May be used, copied, altered or sold freely under the same terms a +nd conditions as Perl itself. =head1 Demo/test program. The following code serves as both demo program and test suite. To run the integrated version switch to the directory where the mo +dule is located and type: =begin text perl -x =end text The program should respond with: =begin text E:\Perl\site\lib\My>perl -x Do what? E:\Perl\site\lib\My> =end =cut #! perl use strict; # use My::Module; # Use this instead of the following line in your cod +e. unshift @INC, '.'; require $0; my $test = My::Module->new(); $test->do_something();

Is this a useful idea? Stupid?

Examine what is said, not who speaks.

The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Replies are listed 'Best First'.
Re: RFC: Runnable test code integrated into Modules.
by adrianh (Chancellor) on Jan 30, 2003 at 11:27 UTC

    You might be interested in Test::Inline, which puts test in special POD comments and is cute.

    There's also Test::Extreme which also inlines tests (but isn't cute in my opinion)

    Personally, I'm not to enamoured of inline tests since I find it makes refactoring test suites harder... but other people seem to like it ;-)

      adrianh, could you explain why do you think Test::Extreme is not cute? I was going to use it, but noticed your comment... :)


        Basically because it ignores Test::Builder, and the modules built with it.

        So we have yet another set of testing functions (assert_somethingorother in Test::Extremes case), and no way of adding new kinds of test to the framework easily. Test aren't named either which makes using large test suites a complete pain.

        It also slightly annoys me by saying it's in an xUnit style when it doesn't do most of the things that an xUnit framework does (setting up of test fixtures, composition of test suites, etc.). This kind of thing isn't "OO cruft" as it says in the documentation - they're tools that make some kind of testing a lot easier.

        If you need things like text fixtures then Test::Extreme won't do the job. If you don't need them then there are better testing tools available with Test::More and friends.

        All IMHO of course ;-)

Re: RFC: Runnable test code integrated into Modules.
by gjb (Vicar) on Jan 30, 2003 at 13:59 UTC

    Personally I prefer decoupling the implementation from the code to test it for several reasons.

    • Testing is often about interaction between several modules, in this approach test code gets scattered across modules, and one has to have a script to test the integration of the modules anyway.
    • It's easier to track changes to implementation and test code if they're in separate files. This is especially important during refactoring when the test code must be changed as little as possible, if at all.
    • It's useful to have someone else write the tests for your code since she might think of edge conditions you overlooked. Even if you write your own tests: 'testing' ne 'implementing' and should be as far apart as possible IMHO.
    • If you like to use a testing framework such as Test::More et al., a dependency is created between the module and the testing framework, which is not desirable. (I've been bitten by this at some point.)

    Just my 2 cents, -gjb-

Re: RFC: Runnable test code integrated into Modules.
by jmcnamara (Monsignor) on Jan 30, 2003 at 11:45 UTC

    Another way that I've seen to do this is to use something like:
    return 1 if caller;

    Then running perl will execute the program at the end:

    # -x method $ perl -x Do what? # bare method (not what you want) $ perl Can't return outside a subroutine at line 6. # Now with caller() $ perl Do what?

Re: RFC: Runnable test code integrated into Modules.
by PodMaster (Abbot) on Jan 30, 2003 at 11:41 UTC
    I do it like I do it in Pod::Stripper and HTML::LinkExtractor. Here's the jist of it
    package Buttersquash; sub foo { 'bar' } sub bar { 'foo' } package main; unless(caller()){ print "1..3\n"; print "ok 1\n"; print "not " if Buttersquash->foo ne 'bar'; print "ok 2\n"; print "not " if Buttersquash->bar ne 'foo'; print "ok 3\n"; } =head1 SELF CONTAINED TEST If you already installed this, simply run this oneliner # on nix perl -MButtersquash -e' exec $^X, $INC{q[]}' # on windows perl -MButtersquash -e" exec $^X, $INC{q[]}" or simply perl =cut
    Other already available options is Test::Inline (formerly Pod::Tests) by our favorite b*#@! Schwern ;)

    MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
    ** The Third rule of perl club is a statement of fact: pod is sexy.

Re: RFC: Runnable test code integrated into Modules.
by Anonymous Monk on Jan 31, 2003 at 01:08 UTC
    if (__FILE__ eq $0) { # Load and run test suite here }

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://231256]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (3)
As of 2020-08-11 22:30 GMT
Find Nodes?
    Voting Booth?
    Which rocket would you take to Mars?

    Results (63 votes). Check out past polls.