in reply to TDD: a test drive and a confession

Coming up with tests wasn't easy. The stupid ones that exercise your handling of Getopt::Long were simple. Getting tests that actually exercised code that deals with abstract values ...

I've been writing test driven code (no caps) for years. It started when doing functional verification of the OS/2 apis. 80% percent of the code I wrote for the 3 1/2 years I was involved had only one purpose, to test the apis it used. Writing small sections of code and the test(s) to verify it, as a single unit of work, was the only way to do it. It's one of those things that worked so well for me that I've continued doing it (for the 10+years since) even when it wasn't a strict requirement.

I still find it easier (and equally effective) to write the code that uses the interface, and then devise the mechanism for testing it. I feel that this means that I don't tailor my use of the inteface to fit my testing methods which I have seen happen when doing it the other way around. For any given project, half a dozen or so verification techniques start to repeat and it rapidly becomes second nature to decide which one fits the bill for a particular situation.

This may be less necessary for Perl than C/C++ as the availability of eval means that you can do things in perl that are simply unavailable in C(++), I still think that you need more than the simple ok/nok for most things.

This is key because for any non-trivial test, you're going to have (potentially) non-deterministic output...

One well-used technique with functions that give contiguous ranges of output for discrete sets of input is to look for and validate the transitions across the boundaries, also called edge cases (and corners cases). The trick here is to evolve your tests from the inputs rather than the outputs. That is to say, don't look for where you think (or the code under test pre-determines) that the transitions occur, but rather

That probably sounds horrendously laborious, but for most functions (as in subroutines rather than the mathematical usage), even those with relatively large numbers of inputs (parameters), varying one parameters at a time over limited range(s) whilst iterating the others through the full spread in large steps means that you can usually quickly zero in on the transition points. You sometimes find that when you start varying the second input in smaller steps, that it isolates more (or moves) transitions in the first range, but doing things stepwise usually makes obvious when this is happening and how to compensate.

The idea of reducing the coverage of testcases is an anathema in some circles, but by concentrating on an intelligently selected subset of all possible tests, you can increase the effectiveness of the testing whilst reducing the test time. The shorter the test time, the more frequently it is likely to be run.

As with many things in life, more does not necessarily equal better. Sometimes more is just more, without benefit.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
If I understand your problem, I can solve it! Of course, the same can be said for you.

Replies are listed 'Best First'.
Re: Re: TDD: a test drive and a confession
by liz (Monsignor) on Sep 17, 2003 at 20:15 UTC
    ...by concentrating on an intelligently selected subset of all possible tests, you can increase the effectiveness of the testing whilst reducing the test time. The shorter the test time, the more frequently it is likely to be run.

    To an extent I agree, but not totally. For most of the modules I've written the past year or so, I've found that I actually need tests for 3 contexts:

    1. Does it seem to work in the user's environment?
    This is the test that you want typically run in a CPAN(PLUS) environment when a user installs a module. It should be relatively throrough, but shouldn't take too long.

    2. Does it survive stress testing?
    For most linear programs not really an issue, but if you're working on threaded programs like I have the past year, you know something may appear to work under "normal" situations, but will break down when being hit with everything you got. ;-(

    3. Do all possible combinations of parameters work?
    This is the test that you have internally to make sure that whatever combinations of parameters that can exist, that something valid happens. This is especially important with combinations of command line parameters. This can run as long as you need: for one project I'm working on, this typically runs for 45 minutes: not something you would like your average user to go through.

    Most of my modules on CPAN have at least tests of category 1. Some of them have in category 2 as well. Category 3 tests I only run internally or by a user who is experiencing specific problems, not during a standard test/install sequence.

    Liz