I was clearing out my CPAN RT queue today, and found a question in the tickets for Test::Mock::LWP from dcantrell:
It’s not at all clear how to use this module. I have a module which (partly) wraps around LWP::UserAgent which I use to fetch data which my module then manipulates. Obviously I need to test that my module handles webby errors correctly, for instance that it correctly detects when the remote sites don’t respond; and I need to be able to feed known data to my module so I can test that it does those manipulations correctly.
Test::Mock::LWP is the obvious candidate for faking up LWP::UserAgent, but I just can’t figure out how to use it. Please can you write a HOWTO and add it to the docs.
I’m adding the HOWTO tonight, even though the question was asked 12 years ago (I really need to get to my RT queue more often). The module’s description as it stands is pretty opaque; this explanation should, I hope, make it much more clear.
HOWTO use Test::Mock::LWP
Test::Mock::LWP is designed to provide you a quick way to mock out LWP calls.
Exported variables
Test::Mock::LWP‘s interface is exposed via the variables it exports:
- $Mock_ua – mocks LWP::USerAgent
- $Mock_req / $Mock_request – mocks HTTP::Request
- $Mock_resp / $Mock_response – mocks HTTP::Response
All of these are actually Test::MockObject objects, so you call mock() on them to change how they operate dynamically. Here’s an example.
Let’s say you wanted the next response to an LWP call to return the content foo and an HTTP status code of 201. You’d do this:
BEGIN { # Load the mock modules *first*. use Test::Mock::LWP::UserAgent; use Test::Mock::HTTP::Response; use Test::Mock::HTTP::Request; } # Load the modules you'll use to actually do LWP operations. # These will automatically be mocked for you. use LWP::UserAgent; use HTTP::Response; use HTTP::Request; # Now set up the response you want to get back. $Mock_resp->mock( content => sub { 'foo' }); $Mock_resp->mock( code => sub { 201 }); # Pretend we're making a request to a site. for (1..2) { my $req = HTTP::Request->new(GET => 'http://nevergoeshere.com'); my $agent = LWP::UserAgent->new; my $res = $agent->simple_request($req); } # The values you added to the mock are now there. printf("The site returned %d %s\n", $res->code, $res->content);
This will print
201 foo 201 foo
Getting more than one value out of the mocks: repeated re-mocks
Note that the values are constrained to what you’ve sent to the mocks. The mock here will simply keep returning 201 and foo for as many times as you call it. You’ll need to re-mock the content and code methods
each time you want to change them.
my $req = HTTP::Request->new(GET => 'http://nevergoeshere.com'); my $agent = LWP::UserAgent->new; $Mock_resp->mock( content => sub { 'foo' }); $Mock_resp->mock( code => sub { 201 }); my $res = $agent->simple_request($req); printf("The site returned %d %s\n", $res->code, $res->content); # 201 foo $Mock_resp->mock( content => sub { 'bar' }); $Mock_resp->mock( code => sub { 400 }); my $res = $agent->simple_request($req); printf("The site returned %d %s\n", $res->code, $res->content); # 400 bar
Moving the logic into the mocks
If you have a fixed sequence of items to return, just add them all to the mocks and have the mocks step through them. Here’s an example where we hand off two lists of values to the mocks:
use strict; BEGIN { # Load the mock modules *first*. use Test::Mock::LWP::UserAgent; use Test::Mock::HTTP::Response; use Test::Mock::HTTP::Request; } # Load the modules you'll use to actually do LWP operations. # These will automatically be mocked for you. use LWP::UserAgent; use HTTP::Response; use HTTP::Request; my @contents = qw(foo bar baz); my @codes = qw(404 400 200); # initialize counter. my $code_counter = 2; my $content_counter = 2; my $content_sub = sub { $content_counter += 1; $content_counter %= 3; $contents[$content_counter]; }; my $code_sub = sub { $code_counter += 1; $code_counter %= 3; return $codes[$code_counter]; }; $Mock_resp->mock(content => $content_sub); $Mock_resp->mock(code => $code_sub); my $req = HTTP::Request->new(GET => 'http://nevergoeshere.com'); my $agent = LWP::UserAgent->new; for (0..5) { my $res = $agent->simple_request($req); printf("The site returned %d %s\n", $res->code, $res->content); }
This will print
The site returned 404 foo The site returned 400 bar The site returned 200 baz The site returned 404 foo The site returned 400 bar The site returned 200 baz
Remember: the key is make sure that the mock is ready to return the next item when you make the next request to the user agent.
Leave a Reply
You must be logged in to post a comment.