http://qs321.pair.com?node_id=315658

Trying to impress Santa, who is coming shortly, I pushed myself into studying the Test::* suite (Test::Simple, Test::More, and Test::Harness). Surprisingly, it turned out to be much simpler and faster to adopt, then I had estimated.

After playing around with bits from the Test::Tutorial and few other small and simple things, I decided to go real. :) One of the problems that happen to my code very often is leftovers. When I need to change the way something works, I just copy it over, give it a different name, and play with it, until it really works. Pretty often, as I mentioned, I forget to remove the backup copy, so it stays there doing nothing, being called from nowhere.

Another problem, which occurs less often, but still does, is the robot-like code deletion. If the sub is called rarely, I tend to forget that I need it and once in a while I do delete it and commit to CVS. Of course, that is easy to get back, but it does waste time.

Here is what I decided to do in order to get read of this two problems and to practice the Test::* set of modules a bit more:

#!/usr/bin/perl -w use strict; use Test::More 'no_plan'; # Change these values together # For some reason I didn't manage to use variables here use lib '../lib'; use MyProject; our $project = 'MyProject'; our $lib_dir = '../lib'; # All methods of all modules should be listed here. This # list can actually be generated by another script, but it # will defeat the whole purpose of this meditation. :) my %methods = ( 'ModuleA' => [ 'method_foo', 'method_bar', 'blah', ], 'ModuleB' => [ 'do_things', 'enjoy', ], ); # Firstly, I want to see that I haven't delete any of # the useful staff. foreach my $module (sort keys %methods) { foreach my $method (sort @{$methods{$module}}) { # NOTE: I only check modules which belong to the # project. External stuff is not tested. my $project_module = $project . '::' . $module; can_ok($project_module, $method) or diag("PROBLEM: method $project_module->$method is missing" +); } } # Secondly, I want to see that my tests are realistic, i.e. # that nothing is missing from the %methods. This will # push me either to update the test or remove the leftover # from the code. check_module_tests($project); # It's been a long time since my last recursive function, # btw. :) sub check_module_tests { my $target = shift; my $path_to_target = $lib_dir . '/' . $project; if ($target eq $project) { $path_to_target = $path_to_target . '.pm'; } else { $path_to_target = $path_to_target . '/' . join('/', split('::' +,$target)) } # Read module open (MODULE, "<$path_to_target") or die "$!"; my @module_lines = <MODULE>; close(MODULE); # Look for subs my @subs = grep(/^sub\s+/, @module_lines); # Check that our test suite is complete with all subs foreach my $sub (sort @subs) { chomp($sub); $sub =~ s/^sub\s+(.*?)\{.*$/$1/; $sub =~ s/\s+//g; my $ok_flag = 0; foreach my $test_sub (@{$methods{$target}}) { if ($test_sub eq $sub) { $ok_flag++; } } is ($ok_flag, 1, "No. of occurances of $sub in test suite") or diag('PROBLEM: ' . $target . '->' . $sub . ' is not in tes +t suite'); } # Look for modules my @uses = grep(/^use\s+${project}/, @module_lines); # Recursively test each module foreach my $use (sort @uses) { chomp($use); $use =~ s/^use\s+${project}::(.*?);/$1/; if (defined($methods{$use})) { check_module_tests($use); } # Don't complain about subs of modules which are not # in %methods themselves - that's obvious. :) else { fail ("Is module $use in test suite?"); diag ("PROBLEM: $target uses $use, but $use is not in test + suite"); } } }

It turned out to help me a lot. Maybe someone else will benefit from this. Note though, that you will either have to code in the same style that I do ("use" and "sub" are at the beginning of the line, blah, blah, blah) or change the regular expressions that match the code.

P.S.: Merry coming Christmas! :)

Edited by BazB: added readmore tag.