Sat, 06 Jun 2009

Testing with LWP::UserAgent

LWP::UserAgent has a feature that makes testing applications that use it very easy.

I wrote and maintain the Net::Twitter and Net::Twitter::Lite distributions.1 I needed a way to test API calls without actually hitting the Twitter API servers.

The job required of the Net::Twitter modules is to turn simple Perl method calls into HTTP requests and turn the HTTP responses back into useful Perl data. It is, therefore sufficient to inspect an HTTP::Request object at the point it would normally be transmitted to Twitter without actually sending it. And, to return a suitable HTTP::Response object to test the applications behavior.

It turns out, LWP::UserAgent has a callback mechanism that is perfect for this task. The add_handler method takes a phase name, a code reference, and optionally a matchspec to create callbacks at any of several phases. The request_send phase occurs at exactly the right phase for testing: when the HTTP::Request instance is fully configured and ready to send on the wire. If the callback returns an HTTP::Response object, no network call is made. The HTTP::Response provided by the callback is returned to the caller.

Here's an example test using this technique:


    1	use Test::More tests => 2;
    2	use Net::Twitter::Lite;
    3	
    4	my $nt = Net::Twitter::Lite->new;
    5	
    6	my $request;
    7	my %args;
    8	my $response = HTTP::Response->new(200, 'OK');
    9	$response->content('{"test":"success"}');
   10	
   11	$nt->{ua}->add_handler(request_send => sub {
   12	    $request = shift;
   13	
   14	    $response->request($request);
   15	    %args = $request->uri->query_form;
   16	
   17	    return $response;
   18	});
   19	
   20	# additional args in a HASH ref
   21	my $search_term = "intelligent life";
   22	my $r = $nt->search($search_term, { page => 2 });
   23	is $args{q},    $search_term, "q as positional arg";
   24	is $args{page}, 2,            "page parameter set";

In lines 6-7 I declared some lexical variables that will be available in the callback using a closure.

Line 11 sets up the callback on Net::Twitter::Lite's LWP::UserAgent instance.

The callback receives three parameters: the HTTP::Request instance, the LWP::UserAgent instance, and a reference to the callback handler itself. Only the HTTP::Request instance is of interest here. It is assigned to $request on line 12.

An HTTP::Response includes a reference to its initiating HTTP::Request. Line 14 takes care of that.

Since this particular test is dealing with query parameters, they are extracted from the request's URI on line 15 and stored in the HASH declared on line 7.

On line 17, the HTTP::Response is returned to the caller. This prevents LWP::UserAgent from actually making a network call.

Line 22 makes a Net::Twitter::Lite call that should result in an HTTP GET with url http://search.twitter.com/search.json?page=2&q=intelligent+life. Net::Twitter::Lite should inflate the HTTP::Response contents into an appropriate Perl representation of the JSON return.

With the request and response available, a variety of tests can be run to unsure Net::Twitter::Lite is behaving as expected.

You may find this technique useful for your own tests.

[1]Net::Twitter versions 2.12 and earlier were written and maintained by Chris Thompson. Version 3 is a complete rewrite using Moose. Net::Twitter::Lite was created for those who cannot or prefer not to install Moose and its dependencies.

[/perl] [link]

About this weblog

This site is the personal weblog of Marc Mims. You can contact Marc by sending e-mail to:
[email protected].

Marc writes here about cycling, programming, Linux, and other items of personal interest.

This site is syndicated with RSS.

Archives

Credits

CSS stolen from Tom Coates who didn't even complain.