2012 twenty-four merry days of Perl Feed

Testing networking client code using Test::LWP::UserAgent

Test::LWP::UserAgent - 2012-12-12

Test::LWP::UserAgent is a module I wrote after writing several networking client libraries for $work with inconsistent and spotty test coverage — what I most wanted to do was fully simulate the server end of a network connection, without having to delve deeply into LWP's internal implementation, nor mock a lot of methods, which the traditional mock object approach would require.

Exploring the options available led me to Yury Zavarin's Test::Mock::LWP::Dispatch, whose API I adapted into the initial version of Test::LWP::UserAgent. It behaves exactly like LWP::UserAgent, one of the most popular HTTP client libraries in perl, in all respects except for the portion that actually sends the request out to the network - at that point it returns the first response that you have preconfigured that matches the outbound request.

my $useragent = Test::LWP::UserAgent->new;
$useragent->map_response(qr/example.com/, HTTP::Response->new(200));

my $response = $useragent->get('http://example.com');
# prints 200
say $response->code;

$response = $useragent->get('http://google.com');
# prints 404
say $response->code;

In the above example, no outbound reqest passing through this user agent will use the live network (this is the default behaviour). Any request whose URI matches /example.com/ will receive an HTTP 200 response, and the remaining requests will return a 404.

If, however, you wish to capture only some requests, while letting the remainder use the network normally, you can enable the network_fallback feature:

my $useragent = Test::LWP::UserAgent->new;
$useragent->network_fallback(1);
$useragent->map_response(qr/example.com/, HTTP::Response->new(200));

my $response = $useragent->get('http://example.com');
# prints 200
say $response->code;

$response = $useragent->get('http://google.com');
# prints 200 … if Google is up!
say $response->code;

And indeed if you inspect the $response object returned, you will see that it contains the actual network response from contacting http://google.com.

Configuration can also be done globally, if you want all user agents in your program to use the same settings (or if you do not have direct control over the actual user agent object being used, but just its class):

Test::LWP::UserAgent->map_response(...);
my $response = Test::LWP::UserAgent->new->request(...);

Test::LWP::UserAgent inherits from LWP::UserAgent, so it satisfies isa requirements that you may have via Moose or another system that uses type checking. This means that all the normal options available in LWP::UserAgent are still available to you, and work identically, for example:

my $useragent = Test::LWP::UserAgent->new(
    timeout => 10,
    cookie_jar => { file => "$ENV{HOME}/.cookies.txt" },
);

You can also use Test::LWP::UserAgent to connect to a local PSGI application seamlessly, which can be very useful when you have a client and server installed on the same box but do not want to fuss with separate code for handling this case, or if you want more fine-grained control over what responses to send:

my $app = Plack::Util::load_psgi('./myapp.psgi');
$useragent->register_psgi('mytestdomain.com', $app);
my $response = $useragent->request(...);

CODE EXAMPLES

The code examples above are fleshed out as fully-working code in the examples/ directory under http://metacpan.org/release/Test-LWP-UserAgent, along with a detailed example of some unit tests for a hypothetical networking client library.

See Also

Gravatar Image This article contributed by: Karen Etheridge <ether@cpan.org>