1Dancer2::Test(3) User Contributed Perl Documentation Dancer2::Test(3)
2
3
4
6 Dancer2::Test - Useful routines for testing Dancer2 apps
7
9 version 0.400001
10
12 use Test::More;
13 use Plack::Test;
14 use HTTP::Request::Common; # install separately
15
16 use YourDancerApp;
17
18 my $app = YourDancerApp->to_app;
19 my $test = Plack::Test->create($app);
20
21 my $res = $test->request( GET '/' );
22 is( $res->code, 200, '[GET /] Request successful' );
23 like( $res->content, qr/hello, world/, '[GET /] Correct content' );
24
25 done_testing;
26
28 DEPRECATED. This module and all the functions listed below are
29 deprecated. Do not use this module. The routines provided by this
30 module for testing Dancer2 apps are buggy and unnecessary. Instead, use
31 the Plack::Test module as shown in the SYNOPSIS above and ignore the
32 functions in this documentation. Consult the Plack::Test documentation
33 for further details.
34
35 This module will be removed from the Dancer2 distribution in the near
36 future. You should migrate all tests that use it over to the
37 Plack::Test module and remove this module from your system. This module
38 will throw warnings to remind you.
39
40 For now, you can silence the warnings by setting the "NO_WARN" option:
41
42 $Dancer::Test::NO_WARN = 1;
43
44 In the functions below, $test_name is always optional.
45
47 dancer_response ($method, $path, $params, $arg_env);
48 Returns a Dancer2::Core::Response object for the given request.
49
50 Only $method and $path are required.
51
52 $params is a hashref with 'body' as a string; 'headers' can be an
53 arrayref or a HTTP::Headers object, 'files' can be arrayref of hashref,
54 containing some files to upload:
55
56 dancer_response($method, $path,
57 {
58 params => $params,
59 body => $body,
60 headers => $headers,
61 files => [ { filename => '/path/to/file', name => 'my_file' } ],
62 }
63 );
64
65 A good reason to use this function is for testing POST requests. Since
66 POST requests may not be idempotent, it is necessary to capture the
67 content and status in one shot. Calling the response_status_is and
68 response_content_is functions in succession would make two requests,
69 each of which could alter the state of the application and cause
70 Schrodinger's cat to die.
71
72 my $response = dancer_response POST => '/widgets';
73 is $response->status, 202, "response for POST /widgets is 202";
74 is $response->content, "Widget #1 has been scheduled for creation",
75 "response content looks good for first POST /widgets";
76
77 $response = dancer_response POST => '/widgets';
78 is $response->status, 202, "response for POST /widgets is 202";
79 is $response->content, "Widget #2 has been scheduled for creation",
80 "response content looks good for second POST /widgets";
81
82 It's possible to test file uploads:
83
84 post '/upload' => sub { return upload('image')->content };
85
86 $response = dancer_response(POST => '/upload', {files => [{name => 'image', filename => '/path/to/image.jpg'}]});
87
88 In addition, you can supply the file contents as the "data" key:
89
90 my $data = 'A test string that will pretend to be file contents.';
91 $response = dancer_response(POST => '/upload', {
92 files => [{name => 'test', filename => "filename.ext", data => $data}]
93 });
94
95 You can also supply a hashref of headers:
96
97 headers => { 'Content-Type' => 'text/plain' }
98
99 response_status_is ($request, $expected, $test_name);
100 Asserts that Dancer2's response for the given request has a status
101 equal to the one given.
102
103 response_status_is [GET => '/'], 200, "response for GET / is 200";
104
105 route_exists([$method, $path], $test_name)
106 Asserts that the given request matches a route handler in Dancer2's
107 registry. If the route would have returned a 404, the route still
108 exists and this test will pass.
109
110 Note that because Dancer2 uses the default route handler
111 Dancer2::Handler::File to match files in the public folder when no
112 other route matches, this test will always pass. You can disable the
113 default route handlers in the configs but you probably want
114 "response_status_is" in Dancer2::Test or "dancer_response" in
115 Dancer2::Test
116
117 route_exists [GET => '/'], "GET / is handled";
118
119 route_doesnt_exist([$method, $path], $test_name)
120 Asserts that the given request does not match any route handler in
121 Dancer2's registry.
122
123 Note that this test is likely to always fail as any route not matched
124 will be handled by the default route handler in Dancer2::Handler::File.
125 This can be disabled in the configs.
126
127 route_doesnt_exist [GET => '/bogus_path'], "GET /bogus_path is not handled";
128
129 response_status_isnt([$method, $path], $status, $test_name)
130 Asserts that the status of Dancer2's response is not equal to the one
131 given.
132
133 response_status_isnt [GET => '/'], 404, "response for GET / is not a 404";
134
135 response_content_is([$method, $path], $expected, $test_name)
136 Asserts that the response content is equal to the $expected string.
137
138 response_content_is [GET => '/'], "Hello, World",
139 "got expected response content for GET /";
140
141 response_content_isnt([$method, $path], $not_expected, $test_name)
142 Asserts that the response content is not equal to the $not_expected
143 string.
144
145 response_content_isnt [GET => '/'], "Hello, World",
146 "got expected response content for GET /";
147
148 response_content_like([$method, $path], $regexp, $test_name)
149 Asserts that the response content for the given request matches the
150 regexp given.
151
152 response_content_like [GET => '/'], qr/Hello, World/,
153 "response content looks good for GET /";
154
155 response_content_unlike([$method, $path], $regexp, $test_name)
156 Asserts that the response content for the given request does not match
157 the regexp given.
158
159 response_content_unlike [GET => '/'], qr/Page not found/,
160 "response content looks good for GET /";
161
162 response_content_is_deeply([$method, $path], $expected_struct, $test_name)
163 Similar to response_content_is(), except that if response content and
164 $expected_struct are references, it does a deep comparison walking each
165 data structure to see if they are equivalent.
166
167 If the two structures are different, it will display the place where
168 they start differing.
169
170 response_content_is_deeply [GET => '/complex_struct'],
171 { foo => 42, bar => 24},
172 "got expected response structure for GET /complex_struct";
173
174 response_is_file ($request, $test_name);
175 response_headers_are_deeply([$method, $path], $expected, $test_name)
176 Asserts that the response headers data structure equals the one given.
177
178 response_headers_are_deeply [GET => '/'], [ 'X-Powered-By' => 'Dancer2 1.150' ];
179
180 response_headers_include([$method, $path], $expected, $test_name)
181 Asserts that the response headers data structure includes some of the
182 defined ones.
183
184 response_headers_include [GET => '/'], [ 'Content-Type' => 'text/plain' ];
185
186 route_pod_coverage()
187 Returns a structure describing pod coverage in your apps
188
189 for one app like this:
190
191 package t::lib::TestPod;
192 use Dancer2;
193
194 =head1 NAME
195
196 TestPod
197
198 =head2 ROUTES
199
200 =over
201
202 =cut
203
204 =item get "/in_testpod"
205
206 testpod
207
208 =cut
209
210 get '/in_testpod' => sub {
211 return 'get in_testpod';
212 };
213
214 get '/hello' => sub {
215 return "hello world";
216 };
217
218 =item post '/in_testpod/*'
219
220 post in_testpod
221
222 =cut
223
224 post '/in_testpod/*' => sub {
225 return 'post in_testpod';
226 };
227
228 =back
229
230 =head2 SPECIALS
231
232 =head3 PUBLIC
233
234 =over
235
236 =item get "/me:id"
237
238 =cut
239
240 get "/me:id" => sub {
241 return "ME";
242 };
243
244 =back
245
246 =head3 PRIVAT
247
248 =over
249
250 =item post "/me:id"
251
252 post /me:id
253
254 =cut
255
256 post "/me:id" => sub {
257 return "ME";
258 };
259
260 =back
261
262 =cut
263
264 1;
265
266 route_pod_coverage;
267
268 would return something like:
269
270 {
271 't::lib::TestPod' => {
272 'has_pod' => 1,
273 'routes' => [
274 "post /in_testpod/*",
275 "post /me:id",
276 "get /in_testpod",
277 "get /hello",
278 "get /me:id"
279 ],
280 'undocumented_routes' => [
281 "get /hello"
282 ]
283 }
284 }
285
286 is_pod_covered('is pod covered')
287 Asserts that your apps have pods for all routes
288
289 is_pod_covered 'is pod covered'
290
291 to avoid test failures, you should document all your routes with one of
292 the following: head1, head2,head3,head4, item.
293
294 ex:
295
296 =item get '/login'
297
298 route to login
299
300 =cut
301
302 if you use:
303
304 any '/myaction' => sub {
305 # code
306 }
307
308 or
309
310 any ['get', 'post'] => '/myaction' => sub {
311 # code
312 };
313
314 you need to create pods for each one of the routes created there.
315
316 import
317 When Dancer2::Test is imported, it should be passed all the
318 applications that are supposed to be tested.
319
320 If none passed, then the caller is supposed to be the sole application
321 to test.
322
323 # t/sometest.t
324
325 use t::lib::Foo;
326 use t::lib::Bar;
327
328 use Dancer2::Test apps => ['t::lib::Foo', 't::lib::Bar'];
329
331 Dancer Core Developers
332
334 This software is copyright (c) 2023 by Alexis Sukrieh.
335
336 This is free software; you can redistribute it and/or modify it under
337 the same terms as the Perl 5 programming language system itself.
338
339
340
341perl v5.38.0 2023-07-20 Dancer2::Test(3)