|There's more than one way to do things|
Mocking isa under Test::Deepby choroba (Archbishop)
|on May 01, 2016 at 20:01 UTC||Need Help??|
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).
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:
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:
(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:
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:
But alas, this trick doesn't work in the newer Test::Deep. Here's the failure message:
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:
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:
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.
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
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?