Perl Medic: Transforming Legacy Code. Chapter 3. Pt. 2 | WebReference

Perl Medic: Transforming Legacy Code. Chapter 3. Pt. 2

Perl Medic: Transforming Legacy Code. Chapter 3. Pt. 2

This chapter, titled 'Test Now, Test Forever (Diagnosis)' is excerpted from the new book, "Perl Medic: Transforming Legacy Code" (ISBN 0201795264) by Peter J. Scott. This excerpt is posted with permission from publisher Addison-Wesley, copyright 2004, all rights reserved.

3.3 An Example Using Test:: Modules

Let’s put what we’ve learned to use in developing an actual application. Say that we want to create a module that can limit the possible indices of an array, a bounds checker if you will. Perl’s arrays won’t normally do that,6 so we need a mechanism that intercepts the day-to-day activities of an array and checks the indices being used, throwing an exception if they’re outside a specified range. Fortunately, such a mechanism exists in Perl; it’s called tieing, and pretty powerful it is too.

Because our module will work by letting us tie an array to it, we’ll call it Tie::Array::Bounded. We start by letting h2xs do the rote work of creating a new module:

% h2xs -AXn Tie::Array::Bounded Writing
Tie/Array/Bounded/ Writing
Writing Tie/Array/Bounded/README
Writing Tie/Array/Bounded/
Writing Tie/Array/Bounded/Changes
Writing Tie/Array/Bounded/MANIFEST

That saved a lot of time! h2xs comes with perl, so you already have it. Don’t be put off by the name: h2xs was originally intended for creating perl extensions from C header files, a more or less obsolete purpose now, but by dint of copious interface extension, h2xs now enjoys a new lease on life for creating modules. (In Section 8.2.4, I’ll look at a more modern alternative to h2xs.)

Don’t be confused by the fact that the file is in the directory Tie/Array/Bounded. It may look like there’s an extra directory in there but the hierarchy that h2xs created is really just to help keep your sources straight. Everything you create will be in the bottom directory, so we could cd there. For instant gratification we can create a Makefile the way we would with any CPAN module:

6. If you’re smart enough to bring up $[, then you’re also smart enough to know that you shouldn’t be using it.

% cd Tie/Array/Bounded
% perl Makefile.PL
Checking if your kit is complete... Looks good
Writing Makefile for Tie::Array::Bounded

and now we can even run a test:

% make test
cp blib/lib/Tie/Array/ PERL_DL_NONLAZY=1
/usr/local/bin/perl -Iblib/arch -Iblib/lib
I/usr/lib/perl5/5.6.1/i386-linux -I/usr/lib/perl5/5.6.1 test
ok 1

It even passes! This is courtesy of the file that h2xs created for us, which contains a basic test that the module skeleton created by h2xs passes. This is very good for building our confidence. Unfortunately, is not the best way to create tests. We’ll see why when we improve on it by moving into a subdirectory called “t” and rebuilding the Makefile before rerunning “make test”:

% mkdir t
% mv t/01load.t
% perl Makefile.PL
Writing Makefile for Tie::Array::Bounded %
make test
PERL_DL_NONLAZY=1 /usr/local/bin/perl -Iblib/arch -Iblib/lib -
I/usr/lib/perl5/5.6.1/i386-linux -I/usr/lib/perl5/5.6.1 -e 'use
Test::Harness qw(&runtests $verbose); $verbose=0; runtests @ARGV;'
t/*.t t/01load....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.30 cusr + 0.05 csys =
0.35 CPU)

The big difference: “make test” knows that it should run Test::Harness over the .t files in the t subdirectory, thereby giving us a summary of the results.

There’s only one file in there at the moment, but we can create more if we want instead of having to pack every test into

At this point you might want to update the MANIFEST file to remove the line for now that we have removed that file.

If you’re using Perl 5.8.0 or later, then your h2xs has been modernized to create the test in t/1.t; furthermore, it will use Test::More.7 But if you have a prior version of Perl, you’ll find the file we just moved uses the deprecated Test module, so let’s start from scratch and replace the contents of t/01load.t as follows:

use strict;
use warnings;

use Test::More tests => 1;
use blib;
BEGIN { use_ok("Tie::Array::Bounded") }

The use blib statement causes Perl to search in parent directories for a blib directory that contains the Tie/Array/ module created by make. Although we’ll usually run our tests by typing “make test” in the parent directory, this structure for a .t file allows us to run tests individually, which will be helpful when isolating failures.

Running this test either stand-alone (“./01load.t”) or with “make test” produces the same output as before (plus a note from use blib about where it found the blib directory), so let’s move on and add some code to First, delete some code that h2xs put there; we’re not going to export anything, and our code will work on earlier versions of Perl 5, so remove code until the executable part of looks like this:

7. I name my tests with two leading digits so that they will sort properly; I want to run them in a predictable order, and if I have more than nine tests, test 10.t would be run before test 2.t, because of the lexicographic sorting used by the glob() function called by “make test”. Having done that, I can then add text after the digits so that I can also see what the tests are meant for, winding up with test names such as 01load.t.

package Tie::Array::Bounded;
use strict;
use warnings;
our $VERSION = '0.01';

Now it’s time to add subroutines to implement tieing. tie is how to make possessed variables with Perl: Literally anything can happen behind the scenes when the user does the most innocuous thing. A simple expression like $world_peace++ could end up launching a wave of nuclear missiles, if $world_peace happens to be tied to Mutually::Assured::Destruction. (See, you can even use Perl to make covert political statements.)

We need a TIEARRAY subroutine; perltie tells us so. So let’s add an empty one to


and add a test to look for it in 01load.t:

use Test::More tests => 2;
use blib;
BEGIN { use_ok("Tie::Array::Bounded") }

can_ok("Tie::Array::Bounded", "TIEARRAY");

Running “make test” copies the new into blib and produces:

% make test
cp blib/lib/Tie/Array/
PERL_DL_NONLAZY=1 /usr/local/bin/perl -Iblib/arch -Iblib/lib -
I/usr/lib/perl5/5.6.1/i386-linux -I/usr/lib/perl5/5.6.1 -e 'use
Test::Harness qw(&runtests $verbose); $verbose=0; runtests @ARGV;' t/*.t
t/01load....Using /home/peter/perl_Medic/Tie/Array/Bounded/blib
All tests successful.

Files=1, Tests=2, 0 wallclock secs ( 0.29 cusr + 0.02 csys = 0.31 CPU)

We have just doubled our number of regression tests!


Created: March 27, 2003
Revised: March 24, 2004