Spot the Test::Class Bug!

If you run prove on this code, it will fail. Why? Click "Continue Reading" for the answer.

package Check;

use Test::Class::Most parent => 'Test::Class';

use Readonly;
Readonly my $CONSTANT => 4;
INIT { Test::Class->runtests }

sub checkit : Tests(1) {
    is $CONSTANT, 4, 'Constant should be set';
}

1;

OK, this is not really a Test::Class bug. The above code fails because of the INIT block. It will run before the Readonly assignment executes. One way to fix that is with the very clumsy:

package Check;

use Test::Class::Most parent => 'Test::Class';

use Readonly;
my $CONSTANT;
BEGIN { Readonly $CONSTANT => 4; }
INIT { Test::Class->runtests }

sub checkit : Tests(1) {
    is $CONSTANT, 4, 'Constant should be set';
}

1;

This is a common anti-pattern in Test::Class tests. The reason many people don't see that is because they often have a single driver script per class:

use strict;
use warnings;
use Some::Test::Class;

That works because when you "use" a module, the code in that module will be executed and then the INIT block fires, but when you try to execute that module directly, the INIT block fires before the code is run. The semantics are tricky, but they work well once you understand them.

To get around this, I've just uploaded Readonly::BeginLift to the CPAN. Now you can do this:

package Check;

use Test::Class::Most parent => 'Test::Class';

use Readonly::BeginLift;
Readonly my $CONSTANT => 4;
INIT { Test::Class->runtests }

sub checkit : Tests(1) {
    is $CONSTANT, 4, 'Constant should be set';
}

1;

And everything works just fine

2 Comments

Or, you could simply add this to the bottom:


Test::Class->runtests unless caller;

Problem solved.

Leave a comment

About Ovid

user-pic Have Perl; Will Travel. Freelance Perl/Testing/Agile consultant. Photo by http://www.circle23.com/. Warning: that site is not safe for work. The photographer is a good friend of mine, though, and it's appropriate to credit his work.