Ovid has asked for the wisdom of the Perl Monks concerning the following question:

Yesterday I updated a Test::Harness driver program to query Postgres for the names of all of its tables (I need to make this database agnostic). Then, I grab a record count of all of these tables and stuff them into a hash. When the test suite finishes, it grabs the counts again to find out if any of the record counts are different. If so, it prints those to the screen with something like the following:

audit_inventory started with 17763 records and ended with 17766 records

The idea is that the test suite should not change the database (i.e., it should clean up after itself). There are a few problems, though, and I'm trying to think of appropriate strategies to deal with them. The first problem is obvious: those record counts might legitimately change if someone is using the system -- including running tests on another box.

The other problem is more annoying. Because I can specify which tests to exclude, I can do binary searches to narrow down which tests are actually failing, but it would be nice to be able to optionally run the count after every test program is run so the program can tell me where the changes are occurring. Are there any hooks in Test::Harness that allow this? I'm looking for something similar in concept to HARNESS_FILELEAK_IN_DIR which checks after every test whether new files appeared in the directory.

Another problem is that the "changed record count" data only shows up if all tests pass. Is Test::Harness doing something funny with STDOUT that doesn't get cleared up if tests fail?

#!/usr/bin/perl -w use strict; use Test::Harness; use Getopt::Long; use Pod::Usage; use TestConnection qw($dbh); my $sql = <<'END_SQL'; SELECT relname FROM pg_class WHERE relkind = 'r' END_SQL my $sth = $dbh->prepare($sql); $sth->execute; my %tables; while (my $table = $sth->fetchrow_array) { # assumes that no app tables start with pg_ unless ('pg_' eq substr $table, 0, 3) { $tables{$table} = count_records($table); } } sub count_records { my $table = shift; my $sth = $dbh->prepare("SELECT count(*) FROM $table"); $sth->execute; my ($count) = $sth->fetchrow_array; $sth->finish; return $count; } GetOptions( 'help|?' => sub { pod2usage(-verbose => 2); exit }, 'verbose!' => \$Test::Harness::verbose, 'quiet' => sub { $Test::Harness::verbose = 0 }, 'fast' => \$ENV{FAST_TESTS}, 'include=s' => \my @include, 'exclude=s' => \my @exclude ); @include = map { glob } @include; @exclude = map { glob } @exclude; BEGIN { chdir 't' if -d 't'; } my @files = @include; unless (@files) { @files = glob "*.t"; } my @tests; foreach my $file ( sort @files ) { push @tests => $file unless grep { /\Q$file\E/ } @exclude; } runtests( @tests ); print "\n\n"; foreach my $table (sort keys %tables) { my $count = count_records($table); unless ($count == $tables{$table}) { print "$table started with $tables{$table} records and ended w +ith $count records\n"; } } __DATA__ POD snipped

Cheers,
Ovid

New address of my CGI Course.
Silence is Evil (feel free to copy and distribute widely - note copyright text)

Replies are listed 'Best First'.
Re: Interacting with Test::Harness
by dws (Chancellor) on Jun 18, 2003 at 18:59 UTC
    The first problem is obvious: those record counts might legitimately change if someone is using the system -- including running tests on another box.

    Hence the standard technique of running unit tests using a private sandbox database.

    The problem with using record counts as a pass/fail is that it's often difficult to roll back the clock to determine what else was going on in the database while your tests were running. When it gets to be too much of a nuisance to figure out whether a test really failed, you risk having some people adopt a "Oh, yeah, it failed, but that always fails, so we're really O.K." attitude.