For the last two days and nights I have been struggling with a wierd bug in a development version of a module I maintain. There's probably something elementary that I'm missing, so I ask the monks' assistance. I've created a simplified version of the module that reproduces the error. (You can get a complete tarball at http://mysite.verizon.net/jkeen/perl/modules/misc/Woeisme-0.01.tar.gz.

I have a module whose constructor:

  1. Pulls in key-value pairs as arguments and stores them in %parameters.
  2. Then blesses a reference to %default_values.
  3. Then cycles through %parameters and substitutes its values on a key-by-key basis for those in the blessed hash.
  4. Does some elementary validation on some of the values with verify_values(), a subroutine which is designed to accumulate error messages as needed, then print them all out while croaking.
  5. And then returns the reference to the object.

Here is the complete package:

package Woeisme; use strict; local $^W = 1; use vars qw ($VERSION ); $VERSION = 0.01; use Carp; my %default_values = ( SUBJECT => 'Subject (<= 44 characters) goes here', AUTHOR => { NAME => 'A. U. Thor', CPANID => 'AUTHOR', WEBSITE => 'http://a.galaxy.far.far.away/modules', EMAIL => 'a.u.thor@a.galaxy.far.far.away', }, ); sub new { my ($class, %parameters) = @_; my $self = ref($class) ? bless( \%default_values, ref($class) ) : bless( \%default_values, $class ); foreach my $param ( keys %parameters ) { if ( ref( $parameters{$param} ) eq 'HASH' ) { foreach ( keys( %{ $parameters{$param} } ) ) { $self->{$param}{$_} = $parameters{$param}{$_}; } } else { $self->{$param} = $parameters{$param}; } } $self->verify_values(); return ($self); } sub verify_values { my $self = shift; my (@errors); push( @errors, 'NAME is required' ) unless ( $self->{NAME} ); push( @errors, 'Module NAME contains illegal characters' ) unless ( $self->{NAME} and $self->{NAME} =~ m/^[\w:]+$/ ); push( @errors, 'SUBJECTs are limited to 44 characters' ) if ( length( $self->{SUBJECT} ) > 44 ); push( @errors, 'CPAN IDs are 3-9 characters' ) if ( $self->{AUTHOR}{CPANID} !~ m/^\w{3,9}$/ ); push( @errors, 'EMAIL addresses need to have an at sign' ) if ( $self->{AUTHOR}{EMAIL} !~ m/.*\@.*/ ); push( @errors, 'WEBSITEs should start with an "http:" or "https:"' + ) if ( $self->{AUTHOR}{WEBSITE} !~ m/https?:\/\/.*/ ); if (@errors) { croak( join "\n", @errors, '', $!); } } 1;

I'm interested in testing for the cases where bad data is passed to the constructor. Do the proper error messages then appear? Since the real-world module displaying this problem creates directories and files, I try to test each distinct error in its own environment: a temporary directory set up with File::Temp. I have written failsafe() as a wrapper around the creation of the tempdir, the call to the constructor and the test. Here is the test file:

# -*- perl -*- # t/001_load.t - check module loading and create testing directory use Test::More qw{no_plan}; BEGIN { use_ok( 'Woeisme' ); } BEGIN { use_ok( 'File::Temp', qw| tempdir |); } BEGIN { use_ok( 'Cwd' ); } my $object = Woeisme->new ( 'NAME' => 'ABC::XYZ', ); isa_ok ($object, 'Woeisme'); failsafe( [ 'NAME' => 'ABC::XYZ', 'SUBJECT' => '123456789012345678901234567890123456789012345', ], "^SUBJECTs are limited to 44 characters", "Constructor correctly failed due to SUBJECT > 44 characters"); failsafe( [ 'NAME' => 'GHI::DEF', 'AUTHOR' => { NAME => 'James E Keenan', CPANID => 'ABCDEFGHIJ', }, ], "^CPAN IDs are 3-9 characters", "Constructor correctly failed due to CPANID > 9 characters"); sub failsafe { my ($argslistref, $pattern, $message) = @_; my $odir = cwd(); my ($tdir, $mod); $tdir = tempdir( CLEANUP => 1); ok(chdir $tdir, 'changed to temp directory for testing'); local $@ = undef; eval { $mod = Woeisme->new (@$argslistref); }; like($@, qr/$pattern/, $message); local $@ = undef; ok(chdir $odir, 'changed back to original directory after testing') +; }

Since there's only a single test file, once I have maked the package I can test it with either make test or prove -vb t/001_load.t.

Now, here's the problem. If I comment out one or the other of the two failsafe blocks so as to test just one feature at a time, there is no problem. But if I test the two in succession, the like test in the second block fails. It fails only because the wrong message is generated. Instead of getting an error message which begins

CPAN IDs are 3-9 characters

... I'm getting one that first repeats the error message from the previous call to failsafe(), and only then displays the expected error message:

SUBJECTs are limited to 44 characters CPAN IDs are 3-9 characters

I'm getting an error message about the SUBJECT even though I haven't declared any new SUBJECT (and, hence, the subject from %default_values should still prevail -- and that one was less than 44 characters in length). If you sprinkled some print statements around, you'd see that the value of SUBJECT in the second set of tests is 123456789012345678901234567890123456789012345 -- but shouldn't that value have been wiped out (a) because the first constructor failed and (b) because the first set of tests was properly scoped.

Given that each set of tests is limited to the scope of failsafe() and that each set is taking place in its own tempdir, I cannot see any reason why, when I run both test blocks in succession, the value of SUBJECT in the first lingers into the second and eventually causes the wrong error message to print out, causing like to generate a false value.

Any ideas? Thanks in advance.


In reply to Wrong Error Message When Testing for Failure of Constructor by jkeenan1

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.