At work, we use a rather old Perl on the servers (5.10.1 on RedHat 6, but still better than 5.8.3 at my $job-1), with similarly dated distributions. We plan to upgrade, so I tried running some of the tests locally on my laptop with Perl 5.18.2 (openSUSE Leap 42.1).

Hash randomisation

Most failures were caused by the new hash randomisation (see Hash order randomization is coming, are you ready?). As we use Test::Spec, most failures can be solved easily by turning an array reference into a bag:

Mocking objects under Moose

We use Moose in most of the code, which makes mocking a bit harder because of type constraints. Imagine you have the following code you need to test:

use warnings; use strict; { package Person; use Moose; use namespace::autoclean; has name => ( is => 'rw', isa => 'Str', required => 1, ); has id => ( is => 'ro', isa => 'Str', required => 1, ); __PACKAGE__->meta->make_immutable; } { package Position; use Moose; use namespace::autoclean; has person => ( is => 'rw', isa => 'Person', ); has title => ( is => 'ro', isa => 'Str', ); __PACKAGE__->meta->make_immutable; } [download]

When testing the Position, we don't care about the details of the Person. We only want to stub an object with the needed methods implemented, which can even be none:

use Test::Spec; describe 'position' => sub { it 'instantiates' => sub { my $person = stub(); my $position = 'Position'->new(person => $person); isa_ok($position, 'Position'); cmp_deeply([ $position->person ], bag($person)); }; }; [download]

(The last line doesn't make much sense in this context, but imagine more complex objects. We need to use the bag function somewhere to show the problem.)

This would work under plain OO, but it doesn't for us; Moose complains:

Attribute (person) does not pass the type constraint because: Validati + on failed for 'Person' with value Test::Spec::Mocks::MockObject={ } [download]

The common trick to solve this, appearing all over the code base, has been to stub the isa method of the object to always return 1. Moose's got happy when checking the object type, and no one else has cared:

my $person = stub( isa => 1 ); [download]

But alas, this trick doesn't work in the newer Test::Deep. Here's the failure message:

Found a special comparison in $data You can only use specials in the expects structure at /home/choroba/pe + rl5/lib/perl5/Test/Deep.pm line 346. [download]

Pretty informative, don't you think? It took me several hours to find the exact reason; the indicated line was changed in May 2011 in the following way:

# Old: if (! $Expects and ref($d1) and UNIVERSAL::isa($d1, "Test::Deep::Cmp") + ) # New: if (! $Expects and Scalar::Util::blessed($d1) and $d1->isa("Test::Deep + ::Cmp")) [download]

So, returning 1 from the isa method now makes Test::Deep believe the stubbed object is its special construct that shouldn't appear on the left hand side of the comparison.

To verify that's actually the problem, I tried to modify the isa in a more sophisticated way:

my $person = stub( isa => sub { $_[1] !~ /Test/ } ); [download]

and yes, it started to work again. Moose asks for Person , so isa returns 1, the testing framework asks for Test::Deep::Cmp and therefore gets 0.

Final solution

But it's an ugly hack. Some other modules might get confused by such a mocking, as they might check for other classes not containing Test , or, worse, we also have several classes whose namespace contains Test somewhere. So, I created a helper function

sub mock_isa { my ($class) = @_; isa => sub { $_[1] eq $class } } [download]

which can be used as

Not as short as the original trick, but still easier than reimplementing the whole inheritance logic. What tricks do you use?