Perl Medic: Transforming Legacy Code. Chapter 3. Pt. 1
Perl Medic: Transforming Legacy Code. Chapter 3. Pt. 1
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.
“A crash is when your competitor’s program dies. When your program
is an ‘idiosyncrasy’. Frequently, crashes are followed with a message like ‘ID
02’. ‘ID’ is an abbreviation for idiosyncrasy and the number that follows
indicates how many more months of testing the product should have had.”
— Guy Kawasaki
This chapter might appear at first blush to be out of sequence. We’re steadily getting more specific in the details of taking over code, and here near the beginning is a chapter on how to do testing. Shouldn’t it be near the end?
In a word, no. I am going to describe a philosophy of testing that will revolutionize your development practices if you have not already encountered it. I will show you how to implement it for Perl code and give a detailed example. It is so pivotal to the development process that I want to make sure you see it as soon as possible. So yes, it really should come before everything else.
3.1 Testing Your Patience
Here’s the hard part. Creating tests while you’re writing the code for the first time is far, far easier than adding them later on. I know it looks like it should be exactly the same amount of work, but the issue is motivation. When robots are invented that can create code, they won’t have this problem, and the rest of us can mull over this injustice while we’re collecting unemployment pay (except for the guy who invented the robot, who’ll be sipping margaritas on a beach somewhere, counting his royalties and hoping that none of the other program¬mers recognize him).
But we humans don’t like creating tests because it’s not in our nature; we became programmers to exercise our creativity, but “testing” conjures up images of slack-jawed drones looking for defects in bolts passing by them on a conveyor belt.
The good news is that the Test:: modules make it easy enough to overcome this
natural aversion to writing tests at the time you’re developing code.
The point at which you’ve just finished a new function is when your antitesting
hormones are at their lowest ebb because you want to know whether or not it
works. Instead of running a test that gets thrown away, or just staring at the
code long enough to convince yourself that it must work, you can instead write
a real test for it, because it may not require much more effort than typing:
is(some_func("some", "inputs"), qr/some outputs/,
The bad news is that retrofitting tests onto an already complete application requires much more discipline. And if anything could be worse than that, it would be retrofitting tests onto an already complete application that you didn’t write.
There’s no magic bullet that’ll make this problem disappear. The
only course of action that’ll take more time in the long run than writing
tests for your inherited code is not writing them. If you’ve already discovered
the benefits of creating automated tests while writing an application from scratch
then at least you’re aware of how much they can benefit you. I’ll
explore one way to make the test writing more palatable in the next chapter.
3.2 Extreme Testing
This testing philosophy is best articulated by the Extreme Programming (XP) methodology, wherein it is fundamental (see [BECK00]). On the subject of testing, XP says:
Development of tests should precede development of code. All requirements should be turned into tests. All tests should be automated.
The software should pass all its tests at the end of every day. All bugs should get turned into tests.
If you’ve not yet applied these principles to the development of a new
project, you’re in for a life-altering experience when you first give
them an honest try. Because your development speed will take off like a termite
in a lumberyard.
Perl wholeheartedly embraces this philosophy, thanks largely to the efforts in recent years of a group of people including Michael Schwern, chromatic, and others. Because of their enthusiasm and commitment to the testing process, the number of tests that are run when you build Perl from the source and type “make test” has increased from 5,000 in Perl 5.004_04 (1997) to 70,000 in the current development version of Perl 5.9.0 (2004). That’s right, a 14-fold increase.
True to the Perl philosophy, these developers exercised extreme laziness in adding those thousands of tests (see sidebar). To make it easier to create tests for Perl, they created a number of modules that can in fact be used to test anything. We’ll take a look at them shortly.
What is it about this technology that brings such joy to the developer’s heart? It provides a safety net, that’s what. Instead of perennially wondering whether you’ve accidentally broken some code while working on an unrelated piece, you can make certain at any time. If you want to make a radical change to some interface, you can be sure that you’ve fixed all the dependencies because every scenario that you care about will have been captured in a test case, and running all the tests is as simple as typing “make test”. One month into creating a new system that comprised more than a dozen modules and as many programs, I had built up a test suite that ran nearly 600 tests with that one command, all by adding the tests as I created the code they tested. When I made a radical change to convert one interface from functional to object-oriented, it took only a couple of hours because the tests told me when I was done.
This technique has been around for many years, but under the label regression testing, which sounds boring to anyone who can even figure out what it means. 1 However, using that label can be your entrance ticket to respectability when trying to convince managers of large projects that you know what you’re talking about.
1. It’s called regression testing because its purpose is to ensure that no change has caused any part of the program to regress back to an earlier, buggier stage of development.
What’s this about laziness? Isn’t that a pejorative way to describe luminaries of the Perl universe?Actually, no; they’d take it as a compliment. Larry Wall enumerated three principal virtues of Perl programmers:
2. Impatience: There's more than enough work to do in this business. By being impatient to get to the next thing quickly, you'll not spend unnecessary time on the task you're doing; you'll find ways to make it as efficient as possible.
3. Hubris: It's not good enough to be lazy and impatient if you're going to take them as an excuse to do lousy work. You need an unreasonable amount of pride in your abilities to carry you past the many causes for discouragement. If you didn't, and you thought about all the things that could go wrong with your code, you'd either never get out of bed in the morning, or just quit and take up potato farming.
So what are these magic modules that facilitate testing?
Created: March 27, 2003
Revised: March 24, 2004