18.2. Modular Testing
Writing tests always seems like a chore, and an unproductive chore at that: you don't have anything to test yet, so why write tests? And yet, most developers willalmost automaticallywrite driver software to test their new module in an ad hoc way: > cat try_inflections.pl # Test my shiny new English inflections module... use Lingua::EN::Inflect qw( inflect ); # Try some plurals (both standard and unusual inflections)... my %plural_of = ( 'house' => 'houses', 'mouse' => 'mice', 'box' => 'boxes', 'ox' => 'oxen', 'goose' => 'geese', 'mongoose' => 'mongooses', 'law' => 'laws', 'mother-in-law' => 'mothers-in-law', ); # For each of them, print both the expected result and the actual inflection... for my $word ( keys %plural_of ) { my $expected = $plural_of{$word}; my $computed = inflect( "PL_N($word)" ); print "For $word:\n", "\tExpected: $expected\n", "\tComputed: $computed\n"; } A driver like that is actually harder to write than a test suite, because you have to worry about formatting the output in a way that is easy to read. And it's much harder to use the driver than it would be to use a test suite, because every time you run it you have to wade though that formatted output and verify "by eye" that everything is as it should be: > perl try_inflections.pl For house: Expected: houses Computed: houses For law: Expected: laws Computed: laws For mongoose: Expected: mongooses Computed: mongeese For goose: Expected: geese Computed: geese For ox: Expected: oxen Computed: oxen For mother-in-law: Expected: mothers-in-law Computed: mothers-in-laws For mouse: Expected: mice Computed: mice For box: Expected: boxes Computed: boxes That's also error-prone; eyes are not optimized for picking out small differences in the middle of large amounts of nearly identical text. Rather than hacking together a driver program, it's easier to write a test program using the standard Test::Simple module. Instead of print statements showing what's being tested, you just write calls to the ok( ) subroutine, specifying as its first argument the condition under which things are okay, and as its second argument a description of what you're actually testing: > cat inflections.t use Lingua::EN::Inflect qw( inflect ); use Test::Simple qw( no_plan ); my %plural_of = ( 'mouse' => 'mice', 'house' => 'houses', 'ox' => 'oxen', 'box' => 'boxes', 'goose' => 'geese', 'mongoose' => 'mongooses', 'law' => 'laws', 'mother-in-law' => 'mothers-in-law', ); for my $word ( keys %plural_of ) { my $expected = $plural_of{$word}; my $computed = inflect( "PL_N($word)" ); ok( $computed eq $expected, "$word -> $expected" ); } Test programs like this should be kept in files with a .t suffix (inflections.t, conjunctions.t, articles.t, ) and stored in a directory named t/ within your development directory for the application or module. If you set up your development directory using Module::Starter or Module::Starter::PBP (see "Creating Modules" in Chapter 17), this test directory will be set up for you automatically, with some standard .t files already provided. Note that Test::Simple is loaded with the argument qw( no_plan ). Normally that argument would be tests => count, indicating how many tests are expected, but here the tests are generated from the %plural_of table at run time, so the final count will depend on how many entries are in that table. Specifying a fixed number of tests when loading the module is useful if you happen know that number at compile time, because then the module can also "meta-test": verify that you carried out all the tests you expected to. The Test::Simple program is slightly more concise and readable than the original driver code, and the output is much more compact and informative: > perl inflections.t ok 1 - house -> houses ok 2 - law -> laws not ok 3 - mongoose -> mongooses # Failed test (inflections.t at line 21) ok 4 - goose -> geese ok 5 - ox -> oxen not ok 6 - mother-in-law -> mothers-in-law # Failed test (inflections.t at line 21) ok 7 - mouse -> mice ok 8 - box -> boxes 1..8 # Looks like you failed 2 tests of 8. More importantly, this version requires far less effort to verify the correctness of each test. You just scan down the left margin looking for a "not" and a comment line. You might prefer to use the Test::More module instead of Test::Simple. Then you can specify the actual and expected values separately, by using the is( ) subroutine, rather than ok( ): use Lingua::EN::Inflect qw( inflect ); use Test::More qw( no_plan ); Apart from no longer having to type the eq yourself[*], this version also produces more detailed error messages:
> perl inflections.t ok 1 - house -> houses ok 2 - law -> laws not ok 3 - mongoose -> mongooses # Failed test (inflections.t at line 20) # got: 'mongeese' # expected: 'mongooses' ok 4 - goose -> geese ok 5 - ox -> oxen not ok 6 - mother-in-law -> mothers-in-law # Failed test (inflections.t at line 20) # got: 'mothers-in-laws' # expected: 'mothers-in-law' ok 7 - mouse -> mice ok 8 - box -> boxes 1..8 # Looks like you failed 2 tests of 8. The Test::Tutorial documentation that comes with Perl 5.8 provides a gentle introduction to both Test::Simple and Test::More. |