Robert
Robert

Reputation: 8571

Test::MockModule claims "method does not exist"

I try to mock a function in Net::Twitter:

use strict;
use warnings;
use Test::MockModule;

my $mock = Test::MockModule->new('Net::Twitter');
$mock->redefine('update', sub {
    # ...
});

but when I run this, I get:

Net::Twitter::update does not exist! at wtf.pl line 8.

Why is that and how can I actually mock the Net::Twitter::update function?

Upvotes: 2

Views: 377

Answers (1)

simbabque
simbabque

Reputation: 54323

The error message means that it can't find Net::Twitter::update to overwrite. That's pretty clear. The question is, why?.

If you take a look at a Net::Twitter object with Data::Printer, you'll see that there is indeed no update method apparent. In fact, it's not even an object of the type Net::Twitter.

use Net::Twitter;
use Data::Printer;

my $t = Net::Twitter->new;
p $t;

On my machine with the currently newest Net::Twitter installed, it's a Net::Twitter_v4_01042_with__Legacy, which is likely a dynamically created package.

Net::Twitter_v4_01042_with__Legacy  {
    Parents       Net::Twitter::Core
    public methods (22) : apihost, apirealm, apiurl, arrayref_on_error, BUILD, DESTROY, has_error, http_code, http_message, is_list_member, is_list_subscriber, is_subscribed_list, meta, new, searchapiurl, trends, tvhost, tvrealm, tvurl, twittervision, update_twittervision, upload_url
    private methods (5) : _clear_error, _error_return_val, _http_response, _parse_result, _twitter_error
    internals: {
        apirealm               "Twitter API",
        apiurl                 "https://api.twitter.com/1",
        arrayref_on_error      0,
        clientname             "Perl Net::Twitter",
        clienturl              "http://search.cpan.org/dist/Net-Twitter/",
        clientver              4.01042,
        decode_html_entities   0,
        _error_return_val      undef,
        _json_handler          Cpanel::JSON::XS,
        <<MOP>>                Class::MOP::Class::Immutable::Moose::Meta::Class,
        netrc_machine          "api.twitter.com",
        searchapiurl           "https://search.twitter.com",
        source                 "twitterpm",
        ssl                    1,
        tvhost                 "twittervision.com:80",
        tvrealm                "Web Password",
        tvurl                  "http://twittervision.com",
        twittervision          0,
        upload_url             "https://upload.twitter.com/1",
        useragent              "Net::Twitter/4.01042 (Perl)",
        useragent_args         {},
        useragent_class        "LWP::UserAgent"
    }
}

If you try looking for a sub update in the entire Net::Twitter distribution, you'll find nothing. It's not part of a role, it just doesn't exist.

But that does not mean it's not there. I dug a little deeper, and found an update in Net::Twitter::Role::API::REST. It's defined using the twitter_api_method function from Net::Twitter::API. It does a bunch of things and then uses Moose's meta layer to install the method into some class – probably Net::Twitter_v4_01042_with__Legacy on my machine` – and even brings its own meta method class Net::Twitter::Meta::Method.

So it's fairly obvious why Test::MockModule wouldn't find Net::Twitter::update. It simply never

But how do we mock it for testing?

I believe the simplest approach, though certainly not a very nice one, is to overwrite update in the package that it exists in. For that, you need to create an object and check.

use strict;
use warnings;
use Test::MockModule;
use Net::Twitter;

my $mock = Test::MockModule->new( ref( Net::Twitter->new ) );
$mock->redefine(
    'update',
    sub {
         ... # will die with 'unimplemented'
    }
);

my $nt = Net::Twitter->new->update;

This code is a little wasteful, because it needs to create an object just to check what package it ended up in, but it works.

Unimplemented at /home/simbabque/code/scratch/scratch.pl line 236.

Upvotes: 5

Related Questions