Create branch register_actions.
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Test.pm
CommitLineData
fc7ec1d9 1package Catalyst::Test;
2
3use strict;
b39840da 4use warnings;
ae29b412 5use Test::More ();
d837e1a7 6
a2f2cde9 7use Catalyst::Exception;
d837e1a7 8use Catalyst::Utils;
ae29b412 9use Class::MOP;
10use Sub::Exporter;
11
12my $build_exports = sub {
13 my ($self, $meth, $args, $defaults) = @_;
14
15 my $request;
16 my $class = $args->{class};
17
18 if ( $ENV{CATALYST_SERVER} ) {
19 $request = sub { remote_request(@_) };
20 } elsif (! $class) {
21 $request = sub { Catalyst::Exception->throw("Must specify a test app: use Catalyst::Test 'TestApp'") };
22 } else {
23 unless (Class::MOP::is_class_loaded($class)) {
24 Class::MOP::load_class($class);
25 }
26 $class->import;
27
28 $request = sub { local_request( $class, @_ ) };
29 }
30
31 my $get = sub { $request->(@_)->content };
32
33 return {
34 request => $request,
35 get => $get,
36 content_like => sub {
37 my $action = shift;
38 return Test::More->builder->like($get->($action),@_);
39 },
40 action_ok => sub {
41 my $action = shift;
42 return Test::More->builder->ok($request->($action)->is_success, @_);
43 },
44 action_redirect => sub {
45 my $action = shift;
46 return Test::More->builder->ok($request->($action)->is_redirect,@_);
47 },
48 action_notfound => sub {
49 my $action = shift;
50 return Test::More->builder->is_eq($request->($action)->code,404,@_);
51 },
52 contenttype_is => sub {
53 my $action = shift;
54 my $res = $request->($action);
55 return Test::More->builder->is_eq(scalar($res->content_type),@_);
56 },
57 };
58};
59
60our $default_host;
61
62{
63 my $import = Sub::Exporter::build_exporter({
64 groups => [ all => $build_exports ],
65 into_level => 1,
66 });
67
68
69 sub import {
70 my ($self, $class, $opts) = @_;
71 $import->($self, '-all' => { class => $class });
72 $opts = {} unless ref $opts eq 'HASH';
73 $default_host = $opts->{default_host} if exists $opts->{default_host};
74 }
75}
0f895006 76
fc7ec1d9 77=head1 NAME
78
8d2fa70c 79Catalyst::Test - Test Catalyst Applications
fc7ec1d9 80
81=head1 SYNOPSIS
82
49faa307 83 # Helper
49faa307 84 script/test.pl
85
fc7ec1d9 86 # Tests
87 use Catalyst::Test 'TestApp';
88 request('index.html');
89 get('index.html');
90
7ef58180 91 use HTTP::Request::Common;
92 my $response = request POST '/foo', [
93 bar => 'baz',
94 something => 'else'
95 ];
96
45374ac6 97 # Run tests against a remote server
21465c88 98 CATALYST_SERVER='http://localhost:3000/' prove -r -l lib/ t/
45374ac6 99
b6898a9f 100 # Tests with inline apps need to use Catalyst::Engine::Test
101 package TestApp;
102
8d2fa70c 103 use Catalyst;
b6898a9f 104
c46c32fa 105 sub foo : Global {
b6898a9f 106 my ( $self, $c ) = @_;
107 $c->res->output('bar');
c46c32fa 108 }
109
110 __PACKAGE__->setup();
b6898a9f 111
112 package main;
113
b6898a9f 114 use Catalyst::Test 'TestApp';
ae29b412 115 use Test::More tests => 1;
b6898a9f 116
117 ok( get('/foo') =~ /bar/ );
118
ae29b412 119 # mock virtual hosts
120 use Catalyst::Test 'MyApp', { default_host => 'myapp.com' };
121 like( get('/whichhost'), qr/served by myapp.com/ );
122 like( get( '/whichhost', { host => 'yourapp.com' } ), qr/served by yourapp.com/ );
123 {
124 local $Catalyst::Test::default_host = 'otherapp.com';
125 like( get('/whichhost'), qr/served by otherapp.com/ );
126 }
127
fc7ec1d9 128=head1 DESCRIPTION
129
7ef58180 130This module allows you to make requests to a Catalyst application either without
131a server, by simulating the environment of an HTTP request using
132L<HTTP::Request::AsCGI> or remotely if you define the CATALYST_SERVER
ae29b412 133environment variable. This module also adds a few catalyst
134specific testing methods as displayed in the method section.
7ef58180 135
136The </get> and </request> functions take either a URI or an L<HTTP::Request>
137object.
fc7ec1d9 138
139=head2 METHODS
140
b5ecfcf0 141=head2 get
fc7ec1d9 142
143Returns the content.
144
145 my $content = get('foo/bar?test=1');
146
f13fc03f 147Note that this method doesn't follow redirects, so to test for a
148correctly redirecting page you'll need to use a combination of this
149method and the L<request> method below:
150
151 my $res = request('/'); # redirects to /y
152 warn $res->header('location');
153 use URI;
154 my $uri = URI->new($res->header('location'));
155 is ( $uri->path , '/y');
156 my $content = get($uri->path);
157
b5ecfcf0 158=head2 request
fc7ec1d9 159
ae29b412 160Returns a C<HTTP::Response> object. Accepts an optional hashref for request
161header configuration; currently only supports setting 'host' value.
fc7ec1d9 162
795117cf 163 my $res = request('foo/bar?test=1');
ae29b412 164 my $virtual_res = request('foo/bar?test=1', {host => 'virtualhost.com'});
45374ac6 165
b5ecfcf0 166=head2 local_request
0f895006 167
7ef58180 168Simulate a request using L<HTTP::Request::AsCGI>.
169
0f895006 170=cut
171
172sub local_request {
173 my $class = shift;
174
175 require HTTP::Request::AsCGI;
176
177 my $request = Catalyst::Utils::request( shift(@_) );
ae29b412 178 _customize_request($request, @_);
0f895006 179 my $cgi = HTTP::Request::AsCGI->new( $request, %ENV )->setup;
180
181 $class->handle_request;
182
183 return $cgi->restore->response;
184}
185
523d44ec 186my $agent;
187
b5ecfcf0 188=head2 remote_request
bea4160a 189
b77e7869 190Do an actual remote request using LWP.
bea4160a 191
192=cut
193
45374ac6 194sub remote_request {
45374ac6 195
68eb5874 196 require LWP::UserAgent;
197
d837e1a7 198 my $request = Catalyst::Utils::request( shift(@_) );
0f895006 199 my $server = URI->new( $ENV{CATALYST_SERVER} );
523d44ec 200
ae29b412 201 _customize_request($request, @_);
202
523d44ec 203 if ( $server->path =~ m|^(.+)?/$| ) {
890e8d18 204 my $path = $1;
205 $server->path("$path") if $path; # need to be quoted
f4c0f6f7 206 }
cdae055a 207
208 # the request path needs to be sanitised if $server is using a
209 # non-root path due to potential overlap between request path and
210 # response path.
211 if ($server->path) {
f4c0f6f7 212 # If request path is '/', we have to add a trailing slash to the
213 # final request URI
214 my $add_trailing = $request->uri->path eq '/';
215
cdae055a 216 my @sp = split '/', $server->path;
217 my @rp = split '/', $request->uri->path;
218 shift @sp;shift @rp; # leading /
219 if (@rp) {
220 foreach my $sp (@sp) {
a7daf37e 221 $sp eq $rp[0] ? shift @rp : last
cdae055a 222 }
223 }
224 $request->uri->path(join '/', @rp);
f4c0f6f7 225
226 if ( $add_trailing ) {
227 $request->uri->path( $request->uri->path . '/' );
228 }
523d44ec 229 }
230
231 $request->uri->scheme( $server->scheme );
232 $request->uri->host( $server->host );
233 $request->uri->port( $server->port );
234 $request->uri->path( $server->path . $request->uri->path );
235
68eb5874 236 unless ($agent) {
9ffadf88 237
d837e1a7 238 $agent = LWP::UserAgent->new(
523d44ec 239 keep_alive => 1,
240 max_redirect => 0,
241 timeout => 60,
451553f5 242
243 # work around newer LWP max_redirect 0 bug
244 # http://rt.cpan.org/Ticket/Display.html?id=40260
245 requests_redirectable => [],
523d44ec 246 );
d837e1a7 247
523d44ec 248 $agent->env_proxy;
249 }
45374ac6 250
251 return $agent->request($request);
fc7ec1d9 252}
253
ae29b412 254sub _customize_request {
255 my $request = shift;
256 my $opts = pop(@_) || {};
257 $opts = {} unless ref($opts) eq 'HASH';
258 if ( my $host = exists $opts->{host} ? $opts->{host} : $default_host ) {
259 $request->header( 'Host' => $host );
260 }
261}
262
263=head2 action_ok
264
265Fetches the given url and check that the request was successful
266
267=head2 action_redirect
268
269Fetches the given url and check that the request was a redirect
270
271=head2 action_notfound
272
273Fetches the given url and check that the request was not found
274
275=head2 content_like
276
277Fetches the given url and matches the content against it.
278
279=head2 contenttype_is
280
281Check for given mime type
282
fc7ec1d9 283=head1 SEE ALSO
284
7ef58180 285L<Catalyst>, L<Test::WWW::Mechanize::Catalyst>,
286L<Test::WWW::Selenium::Catalyst>, L<Test::More>, L<HTTP::Request::Common>
fc7ec1d9 287
0bf7ab71 288=head1 AUTHORS
fc7ec1d9 289
0bf7ab71 290Catalyst Contributors, see Catalyst.pm
fc7ec1d9 291
292=head1 COPYRIGHT
293
294This program is free software, you can redistribute it and/or modify it under
295the same terms as Perl itself.
296
297=cut
298
2991;