File Coverage

blib/lib/Test/HTTP.pm
Criterion Covered Total %
statement 74 108 68.5
branch 11 20 55.0
condition 3 23 13.0
subroutine 20 28 71.4
pod 13 13 100.0
total 121 192 63.0


line stmt bran cond sub pod time code
1             package Test::HTTP;
2 4     4   65693 use warnings;
  4         7  
  4         118  
3 4     4   22 use strict;
  4         7  
  4         221  
4              
5             our $VERSION = 0.18;
6              
7             =head1 NAME
8              
9             Test::HTTP - Test HTTP interactions.
10              
11             =head1 SYNOPSIS
12            
13             use Test::HTTP tests => 9;
14              
15             {
16             my $uri = "$BASE/data/page/Foo_Bar_Baz";
17             my $type = 'text/x.waki-wiki';
18             my $test = Test::HTTP->new('HTTP page creation and deletion');
19              
20             $test->get($uri, [Accept => $type]);
21             $test->status_code_is(404, "Page not yet there.");
22              
23             $test->put($uri, ['Content-type' => $type], 'xyzzy');
24             $test->status_code_is(201, "PUT returns 201."); # Created
25             $test->header_is(
26             'Content-type' => $type,
27             "Content-type matches on PUT.");
28             $test->header_like(
29             Location => qr{^$BASE/data/page/},
30             "Created page location makes sense.");
31             $test->body_is('xyzzy');
32              
33             $test->get($uri, [Accept => $type]);
34             $test->status_code_is(200, "Page is now there.");
35             $test->header_is(
36             'Content-type' => $type,
37             "Content-type matches on GET.");
38             $test->body_is('xyzzy');
39              
40             $test->delete($uri);
41             $test->status_code_is(204, "DELETE returns 204."); # No content
42             }
43              
44             =head1 DESCRIPTION
45              
46             L is designed to make it easier to write tests which are mainly
47             about HTTP-level things, such as REST-type services.
48              
49             Each C object can contain state about a current request and its
50             response. This allows convenient shorthands for sending requests, checking
51             status codes, headers, and message bodies.
52              
53             =cut
54              
55 4     4   20 use base 'Exporter';
  4         10  
  4         454  
56 4     4   20 use Carp 'croak';
  4         5  
  4         279  
57 4     4   3261 use Class::Field 'field';
  4         104186  
  4         316  
58 4     4   42 use Encode qw(encode_utf8 is_utf8);
  4         7  
  4         319  
59 4     4   4737 use Filter::Util::Call;
  4         4759  
  4         301  
60 4     4   3580 use HTTP::Request;
  4         196059  
  4         165  
61 4     4   3953 use Test::Builder;
  4         42198  
  4         9037  
62              
63             our $Builder = Test::Builder->new;
64             our $BasicPassword;
65             our $BasicUsername;
66             our $UaClass = 'LWP::UserAgent';
67             our $TODO = undef;
68             our @EXPORT = qw($TODO);
69              
70             sub _partition(&@);
71              
72             sub import {
73 4     4   41 my $class = shift;
74              
75 4         36 $Builder->exported_to(scalar caller);
76              
77 4     8   56 my ( $syntax, $nargs ) = _partition { $_ eq '-syntax' } @_;
  8         43  
78 4         34 $Builder->plan(@$nargs);
79              
80             # WARNING: This only exports the stuff in @EXPORT.
81 4         1389 $class->export_to_level(1, $class);
82              
83 4 100       2758 if (@$syntax) {
84 2         7 @_ = ();
85 2         1493 require Test::HTTP::Syntax;
86 2         20 goto &Test::HTTP::Syntax::import;
87             }
88             }
89              
90             =head1 CONSTRUCTOR
91              
92             =head2 Test::HTTP->new($name);
93              
94             C<$name> is a name for the test, used to help write test descriptions when you
95             don't specify them.
96              
97             =cut
98              
99             sub new {
100 6     6 1 3824 my $class = shift;
101              
102 6         25 my $new_object = bless {}, $class;
103 6         40 $new_object->_initiliaze(@_);
104 6         117 return $new_object;
105             }
106              
107             sub _initiliaze {
108 6     6   18 my ( $self, $name ) = @_;
109              
110 6         583 $self->name($name);
111             }
112              
113             # Given a predicate and a list, return two listrefs. The elements in the
114             # first listref satisfy the predicate, and those in the second do not. The
115             # predicate acts on a localized value of $_ rather than any arguments to it.
116             sub _partition(&@) {
117 4     4   14 my ( $pred, @l ) = @_;
118 4         12 my ( $tl, $fl ) = ( [], [] );
119              
120 4 100       18 push @{ &$pred ? $tl : $fl }, $_ for @l;
  8         16  
121              
122 4         13 return ( $tl, $fl );
123             }
124              
125             =head1 OBJECT FIELDS
126              
127             You can get/set any of these by saying C<< $test->foo >> or
128             C<< $test->foo(5) >>, respectively.
129              
130             =head2 $test->name
131              
132             The name for the test.
133              
134             =head2 $test->request
135              
136             The current L being constructed or most recently sent.
137              
138             =head2 $test->response
139              
140             The most recently received L.
141              
142             =head2 $test->ua
143              
144             The User Agent object (usually an L).
145              
146             =head2 $test->username
147              
148             =head2 $test->password
149              
150             A username and password to be used for HTTP basic auth. Default to the values
151             of C<$Test::HTTP::BasicUsername> and C<$Test::HTTP::BasicPassword>,
152             respectively. If both are undef, then authentication is not attempted.
153              
154             =cut
155              
156             field 'name';
157             field 'request';
158             field 'response';
159             field 'ua', -init => '$self->_ua_class->new';
160             field 'username', -init => '$Test::HTTP::BasicUsername';
161             field 'password', -init => '$Test::HTTP::BasicPassword';
162              
163             =head1 REQUEST METHODS
164              
165             =head2 head, get, put, post, and delete
166              
167             Any of these methods may be used to do perform the expected HTTP request.
168             They are all equivalent to
169              
170             $obj->run_request(METHOD => ARGS);
171              
172             =cut
173              
174             sub head {
175 0     0 1 0 my $self = shift;
176 0         0 $self->run_request(HEAD => @_);
177             }
178              
179             sub get {
180 3     3 1 20 my $self = shift;
181 3         12 $self->run_request(GET => @_);
182             }
183              
184             sub put {
185 0     0 1 0 my $self = shift;
186 0         0 $self->run_request(PUT => @_);
187             }
188              
189             sub post {
190 0     0 1 0 my $self = shift;
191 0         0 $self->run_request(POST => @_);
192             }
193              
194             sub delete {
195 0     0 1 0 my $self = shift;
196 0         0 $self->run_request(DELETE => @_);
197             }
198              
199             =head2 $test->run_request([METHOD => $uri [, $headers [, $content]]]);
200              
201             If there are any arguments, they are all passed to the L
202             constructor to create a new C<< $test->request >>.
203              
204             C<< $test->request >> is then executed, and C<< $test->response >> will hold
205             the resulting L.
206              
207             =cut
208              
209             sub run_request {
210 6     6 1 49 my ( $self, @request_args ) = @_;
211 6 100       33 $self->new_request(@request_args) if @request_args;
212 6 50       168 if ($self->request->method ne 'GET') {
213 0 0       0 if (is_utf8($self->request->content)) {
214 0         0 my $content = $self->request->content;
215 0         0 $content = encode_utf8($content);
216 0         0 $self->request->content($content);
217             }
218             }
219              
220 6         253 $self->response( $self->ua->simple_request( $self->request ) );
221 6 50       3156484 croak( $self->request->uri . ': ' . $self->response->status_line )
222             if $self->response->status_line =~ /500 Can't connect to /;
223 6         356 return $self->response;
224             }
225              
226             =head2 $test->new_request(METHOD => $uri [, $headers [, $content]]);
227              
228             Set up a new request object as in run_request, but do not execute it yet.
229             This is handy if you want to call assorted methods on the request to tweak it
230             before running it with C<< $test->run_request >>.
231              
232             =cut
233              
234             sub new_request {
235 6     6 1 30 my ( $self, $method, $uri, @args ) = @_;
236 6         57 $self->request(
237             HTTP::Request->new( $method => $uri, @args ) );
238 6 50 33     30838 $self->request->authorization_basic($self->username, $self->password)
239             if (defined $self->username) || (defined $self->password);
240 6         574 return $self->request;
241             }
242              
243             =head1 TEST METHODS
244              
245             =head2 $test->status_code_is($code [, $description]);
246              
247             Compares the last response status code with the given code using
248             Cis>.
249              
250             =cut
251              
252             sub status_code_is {
253 6     6 1 104 local $Test::Builder::Level = $Test::Builder::Level + 1;
254 6         19 my ( $self, $expected_code, $description ) = @_;
255              
256 6   33     176 $description ||= $self->name . " status is $expected_code.";
257              
258 6         395 $Builder->is_eq( $self->response->code, $expected_code, $description );
259             }
260              
261             =head2 $test->header_is($header_name, $value [, $description]);
262              
263             Compares the response header C<$header_name> with the value C<$value> using
264             Cis>.
265              
266             =cut
267              
268             sub header_is {
269 0     0 1 0 local $Test::Builder::Level = $Test::Builder::Level + 1;
270 0         0 my ( $self, $header_name, $expected_value, $description ) = @_;
271              
272 0   0     0 $description ||= $self->name . " $header_name matches '$expected_value'.";
273              
274 0         0 $Builder->is_eq(
275             scalar $self->response->header($header_name),
276             $expected_value,
277             $description
278             );
279             }
280              
281             =head2 $test->header_like($header_name, $regex, [, $description]);
282              
283             Compares the response header C<$header_name> with the regex C<$regex> using
284             Clike>.
285              
286             =cut
287              
288             sub header_like {
289 1     1 1 894 local $Test::Builder::Level = $Test::Builder::Level + 1;
290 1         3 my ( $self, $header_name, $regex, $description ) = @_;
291              
292 1   33     37 $description ||= $self->name . " $header_name matches $regex.";
293              
294 1         45 $Builder->like(
295             scalar $self->response->header($header_name),
296             $regex,
297             $description
298             );
299             }
300              
301             =head2 $test->body_is($expected_body [, $description]);
302              
303             Verifies that the HTTP response body is exactly C<$expected_body>.
304              
305             =cut
306              
307             sub body_is {
308 0     0 1 0 local $Test::Builder::Level = $Test::Builder::Level + 1;
309 0         0 my ( $self, $expected_body, $description ) = @_;
310              
311 0   0     0 $description ||= $self->name . " body is '$expected_body'.";
312              
313 0         0 $Builder->is_eq( $self->_decoded_content, $expected_body, $description );
314             }
315              
316             =head2 $test->body_like($regex [, $description]);
317              
318             Compares the HTTP response body with C<$regex>.
319              
320             =cut
321              
322             sub body_like {
323 0     0 1 0 local $Test::Builder::Level = $Test::Builder::Level + 1;
324 0         0 my ( $self, $regex, $description ) = @_;
325              
326 0   0     0 $description ||= $self->name . " body matches $regex.";
327              
328 0         0 $Builder->like($self->_decoded_content, $regex, $description);
329             }
330              
331             =head1 USER AGENT GENERATION
332              
333             The user agent (UA) is created when the C object is constructed.
334             By default, L is used to create this object, but it may be
335             handy to test your HTTP handlers without going through an actual HTTP server
336             (for speed, e.g.), so there are a couple of ways to override the chosen class.
337              
338             If the environment variable C is set, this value is used
339             instead. If not, then the current value of C<$Test::HTTP::UaClass>
340             (C by default) is used. Thus, the incantation below may prove
341             useful.
342              
343             {
344             local $Test::HTTP::UaClass = 'MyCorp::REST::FakeUserAgent';
345             my $test = Test::HTTP->new("widget HTTP access");
346             # ...
347             }
348              
349             =cut
350              
351             sub _ua_class {
352 6     6   73 my $self = shift;
353              
354 6 50       261 my $class = exists $ENV{TEST_HTTP_UA_CLASS}
355             ? $ENV{TEST_HTTP_UA_CLASS}
356             : $UaClass;
357              
358 6         394 eval "require $class";
359 6 50       113155 die if $@;
360 6         50 $class->import;
361              
362 6         51 return $class;
363             }
364              
365             sub _decoded_content {
366 0     0     my $self = shift;
367 0           my $content = $self->response->decoded_content;
368            
369             # Work around a bug in HTTP::Message where only text or xml content types
370             # are decoded
371 0           my $response = $self->response;
372 0           my $ct = $self->response->header("Content-Type");
373 0 0 0       unless ($response->content_is_text or $response->content_is_xml) {
374 0           my ($charset) = $ct =~ m{charset=(\S+)};
375 0   0       $charset ||= "ISO-8859-1";
376 0           require Encode;
377 0           $content = Encode::decode($charset, $content);
378             }
379              
380 0           return $content;
381             }
382              
383             =head1 SEE ALSO
384              
385             L,
386             L,
387             L,
388             L,
389             L,
390             L
391              
392             =head1 AUTHOR
393              
394             Socialtext, Inc. C<< >>
395              
396             =head1 COPYRIGHT & LICENSE
397              
398             Copyright 2006 Socialtext, Inc., all rights reserved.
399              
400             Same terms as Perl.
401              
402             =cut
403              
404             1;