Testing Archives

DBD::Mock ... still maintained?

Has DBD::Mock fallen out of favour, love or maintainer energy?

Some recent work at $employer led to me working on a patch for some desired changed to the module.

After hunting down a likely looking repo on github, forking and sending a pull request I noticed that there was one other pull request that’s been sitting there for a year now.

Checking today it looks like the RT ticket queue for the distribution is looking equally unloved.

Is this me volunteering myself as a potential maintainer of the distro? Maybe … if that’s what it takes.

CPAN Testers - "Can't locate FindBin/libs.pm in @INC"

Following my recent update to Catalyst::Plugin::ErrorCatcher I’ve taken some time to scan through the CPAN Testers Reports for the module.

I have to admit to being somewhat perplexed at one of the common test failures [example report]:

Can't locate FindBin/libs.pm in @INC

I’ve specified the library as a ‘TestRequires’ dependency in my Dist::Zilla dist.ini file:

[Prereqs / TestRequires]
DateTime = 0
File::Slurp = 0
FindBin::libs = 0
...

The generated Makefile.PL contains this as a ‘BUILD_REQUIRES’ dependency:

"BUILD_REQUIRES" => {
    # ...
    "File::Slurp" => 0,
    "FindBin::libs" => 0,
    "HTTP::Request::Common" => 0,
    "IO::File" => 0,
    # ...

I’ve also installed ErrorCatcher via cpanm on a perlbrew setup with FindBin::libs missing … and it was installed as a dependency.

There are so many of these reports that it can’t be one or two ‘unusual setups’ as the root cause. Either something has changed, or I’ve done something really stupid during the development of the module.

It’s slightly frustrating only being able to see the output of ‘make test’ and not anything from ‘perl Makefile.PL’.

Previous releases:

  • v0.0.8.9 has the dependency and exhibits the problem
  • v0.0.8.8 lists the dependency and does not exhibit this problem

I like to do as much as I can to resolve issues with my modules installing on various configurations but I’m not really sure where to go next with this one.

It's Easy To Mock

Recently there were cries in the office of:

oh noes! $thingy, which we rely on but don’t control, no longer has a hard-coded, specific record upon which we rely for one of our application tests

Miraculously I had a free slot in my calendar, donned my cape, took off my horn-rimmed glasses and wore my underpant outside my jeans:

I’ll have a look at that for you guys!

Buried Deeply … Somewhere

It turns out that our test was distanced from the problematic remote call by at least one intermediate “helper” module:

our_test.t:

# ...
use Test::OurApp::Data;

# ...
my $thing = Test::OurApp::Data
    ->get_thingy_from_xml( $file );

Test/OurApp/Data.pm:

# ...
use OurApp::ThingyImporter;

# ...
sub get_thingy_from_xml {
    # ...
    $result = import_thingy_ok(
        # ...
    );
    # ...
}

# ...
sub get_thingy_from_xml {
    # ...
    $result = OurApp::ThingyImporter::process_thingy_xml(
        # ...
    );
    # ...
}

OurApp/ThingyImporter.pm:

# ...
sub process_thingy_xml {
    # ...
    $service = OurApp::Domain::Helper->new(
        # ...
    );
    $result = $service->our_annoying_method(
        # ...
    );
    # ...
}

Don’t spend too long worrying about the exact path through the code, just notice that our test is a long way from our_annoying_method()

There wasn’t an obvious or easy way to alter the code to say

We’re testing something else, we don’t care too much about the results

without evil things like test_mode => 1 being passed through the code-tree. Messy and horrible.

Mocking

I’ve heard about mocking objects over the years but have never found a case for using it … until now.

I decided it was time for another look at Test::MockObject.

A short investigation revealed that our_annoying_method() returned a hashref of data used by the rest of the tests … and the tests didn’t care about most of the values.

All I needed to do was inject a method that subverted the problematic method call.

The Solution

The solution was far easier than I anticipated. I wrote a new module, living under t/lib

package Test::OurApp::Mock::Thingy;
use Moose;
    extends 'OurApp::Domain::Thingy';

use Test::MockObject;

# override the troublesome method - we don't really need
# to worry about actual lookups, we just need some data to work with
sub our_annoying_method {
    my $self    = shift;
    my $data    = shift;

    # we don't really care what's returned
    # as long as it has valid looking data
    my $result = {
        requiredData        => $data->{reference}||'CARROT';
        # ...
    };

    return $result;
}

# use Test::MockObject to inject our own method
my $mock = Test::MockObject->new;
$mock->fake_module(
    'OurApp::Domain::Thingy',
    our_annoying_method => \&our_annoying_method,
);

1;

All that was then required was one extra line early in our troubled tests sctipt:

use Test::OurApp::Mock::Thingy;

Tests no longer rely on specific data in someone else’s remote service. Tests pass. Developers happy. Success.

About Chisel

user-pic