As of today, I would go with the following approach to this problem. Imagine this piece of legacy code1, that is not object oriented and cannot be refactored so that it makes dependency injection easy.
package Foo;
use LWP::UserAgent;
sub frobnicate {
return LWP::UserAgent->new->get('http://example.org')->decoded_content;
}
This is indeed tricky to test, and rjh's answer is spot-on. But in 2016 we have a few more modules available than we did back in 2013. I particularly like Sub::Override, which replaces a sub in a given namespace, but only keeps it around in the current scope. That makes it great for unit tests, because you don't need to care about restoring everything after you're done.
package Test::Foo;
use strict;
use warnings 'all';
use HTTP::Response;
use Sub::Override;
use Test::LWP::UserAgent;
use Test::More;
# create a rigged UA
my $rigged_ua = Test::LWP::UserAgent->new;
$rigged_ua->map_response(
qr/\Qexample\E/ => HTTP::Response->new(
'200',
'OK',
[ 'Content-Type' => 'text/plain' ],
'foo',
),
);
# small scope for our override
{
# make LWP return it inside our code
my $sub = Sub::Override->new(
'LWP::UserAgent::new'=> sub { return $rigged_ua }
);
is Foo::frobnicate(), 'foo', 'returns foo';
}
We basically create a Test::LWP::UserAgent object that we feed all of our test cases. We can also give it a code ref that will run tests on the request if we want (not shown here). We then use Sub::Override to make LWP::UserAgent's constructor not return an actual LWP::UA, but the already prepared $rigged_ua
. Then we run our test(s). Once $sub
goes out of scope, LWP::UserAgent::new
gets restored and we do not interfere with anything else.
It's important to always do those tests with the smallest possible scope (as most things in Perl).
If there are a lot of those test cases, it's a good strategy to build some kind of configuration hash for what you expect for each request, and use a building helper function to create the rigged user agent, and another one to create the Sub::Override object. Used in a lexical scope, this approach is very powerful and pretty concise at the same time.
1) represented here by the lack of use strict
and use warnings
.