1Mojolicious::Guides::TeUssteirngC(o3n)tributed Perl DocuMmoejnotlaitciioonus::Guides::Testing(3)
2
3
4

NAME

6       Mojolicious::Guides::Testing - Web Application Testing Made Easy
7

OVERVIEW

9       This document is an introduction to testing web applications with
10       Test::Mojo.  Test::Mojo can be thought of as a module that provides all
11       of the tools and testing assertions needed to test web applications in
12       a Perl-ish way.
13
14       While Test::Mojo can be used to test any web application, it has
15       shortcuts designed to make testing Mojolicious web applications easy
16       and pain-free.
17
18       Please refer to the Test::Mojo documentation for a complete reference
19       to many of the ideas and syntax introduced in this document.
20
21       A test file for a simple web application might look like:
22
23         use Mojo::Base -strict;
24
25         use Test::Mojo;
26         use Test::More;
27
28         # Start a Mojolicious app named "Celestial"
29         my $t = Test::Mojo->new('Celestial');
30
31         # Post a JSON document
32         $t->post_ok('/notifications' => json => {event => 'full moon'})
33           ->status_is(201)
34           ->json_is('/message' => 'notification created');
35
36         # Perform GET requests and look at the responses
37         $t->get_ok('/sunrise')
38           ->status_is(200)
39           ->content_like(qr/ am$/);
40         $t->get_ok('/sunset')
41           ->status_is(200)
42           ->content_like(qr/ pm$/);
43
44         # Post a URL-encoded form
45         $t->post_ok('/insurance' => form => {name => 'Jimmy', amount => '€3.000.000'})
46           ->status_is(200);
47
48         # Use Test::More's like() to check the response
49         like $t->tx->res->dom->at('div#thanks')->text, qr/thank you/, 'thanks';
50
51         done_testing();
52
53       In the rest of this document we'll explore these concepts and others
54       related to Test::Mojo.
55

CONCEPTS

57       Essentials every Mojolicious developer should know.
58
59   Test::Mojo at a glance
60       The Test::More module bundled with Perl includes several primitive test
61       assertions, such as "ok", "is", "isnt", "like", "unlike", "cmp_ok",
62       etc.  An assertion "passes" if its expression returns a true value. The
63       assertion method prints "ok" or "not ok" if an assertion passes or
64       fails (respectively).
65
66       Test::Mojo supplies additional test assertions organized around the web
67       application request/response transaction (transport, response headers,
68       response bodies, etc.), and WebSocket communications.
69
70       One interesting thing of note: the return value of Test::Mojo object
71       assertions is always the test object itself, allowing us to "chain"
72       test assertion methods. So rather than grouping related test statements
73       like this:
74
75         $t->get_ok('/frogs');
76         $t->status_is(200);
77         $t->content_like(qr/bullfrog/);
78         $t->content_like(qr/hypnotoad/);
79
80       Method chaining allows us to connect test assertions that belong
81       together:
82
83         $t->get_ok('/frogs')
84           ->status_is(200)
85           ->content_like(qr/bullfrog/)
86           ->content_like(qr/hypnotoad/);
87
88       This makes for a much more concise and coherent testing experience:
89       concise because we are not repeating the invocant for each test, and
90       coherent because assertions that belong to the same request are
91       syntactically bound in the same method chain.
92
93       Occasionally it makes sense to break up a test to perform more complex
94       assertions on a response. Test::Mojo exposes the entire transaction
95       object so you can get all the data you need from a response:
96
97         $t->put_ok('/bees' => json => {type => 'worker', name => 'Karl'})
98           ->status_is(202)
99           ->json_has('/id');
100
101         # Pull out the id from the response
102         my $newbee = $t->tx->res->json('/id');
103
104         # Make a new request with data from the previous response
105         $t->get_ok("/bees/$newbee")
106           ->status_is(200)
107           ->json_is('/name' => 'Karl');
108
109       The Test::Mojo object is stateful. As long as we haven't started a new
110       transaction by invoking one of the *_ok methods, the request and
111       response objects from the previous transaction are available in the
112       Test::Mojo object:
113
114         # First transaction
115         $t->get_ok('/frogs?q=bullfrog' => {'Content-Type' => 'application/json'})
116           ->status_is(200)
117           ->json_like('/0/species' => qr/catesbeianus/i);
118
119         # Still first transaction
120         $t->content_type_is('application/json');
121
122         # Second transaction
123         $t->get_ok('/frogs?q=banjo' => {'Content-Type' => 'text/html'})
124           ->status_is(200)
125           ->content_like(qr/interioris/i);
126
127         # Still second transaction
128         $t->content_type_is('text/html');
129
130       This statefulness also enables Test::Mojo to handle sessions, follow
131       redirects, and inspect past responses during a redirect.
132
133   The Test::Mojo object
134       The Test::Mojo object manages the Mojolicious application lifecycle (if
135       a Mojolicious application class is supplied) as well as exposes the
136       built-in Mojo::UserAgent object. To create a bare Test::Mojo object:
137
138         my $t = Test::Mojo->new;
139
140       This object initializes a Mojo::UserAgent object and provides a variety
141       of test assertion methods for accessing a web application. For example,
142       with this object, we could test any running web application:
143
144         $t->get_ok('https://www.google.com/')
145           ->status_is(200)
146           ->content_like(qr/search/i);
147
148       You can access the user agent directly if you want to make web requests
149       without triggering test assertions:
150
151         my $tx = $t->ua->post(
152           'https://duckduckgo.com/html' => form => {q => 'hypnotoad'});
153         $tx->result->dom->find('a.result__a')->each(sub { say $_->text });
154
155       See Mojo::UserAgent for the complete API and return values.
156
157   Testing Mojolicious applications
158       If you pass the name of a Mojolicious application class (e.g., 'MyApp')
159       to the Test::Mojo constructor, Test::Mojo will instantiate the class
160       and start it, and cause it to listen on a random (unused) port number.
161       Testing a Mojolicious application using Test::Mojo will never conflict
162       with running applications, including the application you're testing.
163
164       The Mojo::UserAgent object in Test::Mojo will know where the
165       application is running and make requests to it. Once the tests have
166       completed, the Mojolicious application will be torn down.
167
168         # Listens on localhost:32114 (some unused TCP port)
169         my $t = Test::Mojo->new('Frogs');
170
171       This object initializes a Mojo::UserAgent object, loads the Mojolicious
172       application "Frogs", binds and listens on a free TCP port (e.g.,
173       32114), and starts the application event loop. When the Test::Mojo
174       object ($t) goes out of scope, the application is stopped.
175
176       Relative URLs in the test object method assertions ("get_ok",
177       "post_ok", etc.)  will be sent to the Mojolicious application started
178       by Test::Mojo:
179
180         # Rewritten to "http://localhost:32114/frogs"
181         $t->get_ok('/frogs');
182
183       Test::Mojo has a lot of handy shortcuts built into it to make testing
184       Mojolicious or Mojolicious::Lite applications enjoyable.
185
186       An example
187
188       Let's spin up a Mojolicious application using "mojo generate app
189       MyApp". The "mojo" utility will create a working application and a "t"
190       directory with a working test file:
191
192         $ mojo generate app MyApp
193         [mkdir] /my_app/script
194         [write] /my_app/script/my_app
195         [chmod] /my_app/script/my_app 744
196         ...
197         [mkdir] /my_app/t
198         [write] /my_app/t/basic.t
199         ...
200
201       Let's run the tests (we'll create the "log" directory to quiet the
202       application output):
203
204         $ cd my_app
205         $ mkdir log
206         $ prove -lv t
207         t/basic.t ..
208         ok 1 - GET /
209         ok 2 - 200 OK
210         ok 3 - content is similar
211         1..3
212         ok
213         All tests successful.
214         Files=1, Tests=3,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.33 cusr  0.07
215          csys =  0.44 CPU)
216         Result: PASS
217
218       The boilerplate test file looks like this:
219
220         use Mojo::Base -strict;
221
222         use Test::More;
223         use Test::Mojo;
224
225         my $t = Test::Mojo->new('MyApp');
226         $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
227
228         done_testing();
229
230       Here we can see our application class name "MyApp" is passed to the
231       Test::Mojo constructor. Under the hood, Test::Mojo creates a new
232       Mojo::Server instance, loads "MyApp" (which we just created), and runs
233       the application. We write our tests with relative URLs because
234       Test::Mojo takes care of getting the request to the running test
235       application (since its port may change between runs).
236
237       Testing with configuration data
238
239       We can alter the behavior of our application using environment
240       variables (such as "MOJO_MODE") and through configuration values. One
241       nice feature of Test::Mojo is its ability to pass configuration values
242       directly from its constructor.
243
244       Let's modify our application and add a "feature flag" to enable a new
245       feature when the "enable_weather" configuration value is set:
246
247         # Load configuration from hash returned by "my_app.conf"
248         my $config = $self->plugin('Config');
249
250         # Normal route to controller
251         $r->get('/')->to('example#welcome');
252
253         # NEW: this route only exists if "enable_weather" is set in the configuration
254         if ($config->{enable_weather}) {
255           $r->get('/weather' => sub { shift->render(text => "It's hot! 🔥") }
256         }
257
258       To test this new feature, we don't even need to create a configuration
259       file—we can simply pass the configuration data to the application
260       directly via Test::Mojo's constructor:
261
262         my $t = Test::Mojo->new(MyApp => {enable_weather => 1});
263         $t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
264         $t->get_ok('/weather')->status_is(200)->content_like(qr/🔥/);
265
266       When we run these tests, Test::Mojo will pass this configuration data
267       to the application, which will cause it to create a special "/weather"
268       route that we can access in our tests. Unless "enable_weather" is set
269       in a configuration file, this route will not exist when the application
270       runs. Feature flags like this allow us to do soft rollouts of features,
271       targeting a small audience for a period of time. Once the feature has
272       been proven, we can refactor the conditional and make it a full
273       release.
274
275       This example shows how easy it is to start testing a Mojolicious
276       application and how to set specific application configuration
277       directives from a test file.
278
279       Testing application helpers
280
281       Let's say we register a helper in our application to generate an HTTP
282       Basic Authorization header:
283
284         use Mojo::Util 'b64_encode';
285
286         app->helper(basic_auth => sub {
287           my ($c, @values) = @_;
288           return {Authorization => 'Basic ' . b64_encode join(':' => @values), ''};
289         });
290
291       How do we test application helpers like this? Test::Mojo has access to
292       the application object, which allows us to invoke helpers from our test
293       file:
294
295         my $t = Test::Mojo->new('MyApp');
296
297         is_deeply $t->app->basic_auth(bif => "Bif's Passwerdd"),
298           {Authorization => 'Basic YmlmOkJpZidzIFBhc3N3ZXJkZA=='},
299           'correct header value';
300
301       Any aspect of the application (helpers, plugins, routes, etc.) can be
302       introspected from Test::Mojo through the application object. This
303       enables us to get deep test coverage of Mojolicious-based applications.
304

ASSERTIONS

306       This section describes the basic test assertions supplied by
307       Test::Mojo.  There are four broad categories of assertions for HTTP
308       requests:
309
310       · HTTP requests
311
312       · HTTP response status
313
314       · HTTP response headers
315
316       · HTTP response content/body
317
318       WebSocket test assertions are covered in "Testing WebSocket web
319       services".
320
321   HTTP request assertions
322       Test::Mojo has a Mojo::UserAgent object that allows it to make HTTP
323       requests and check for HTTP transport errors. HTTP request assertions
324       include "get_ok", "post_ok", etc. These assertions do not test whether
325       the request was handled successfully, only that the web application
326       handled the request in an HTTP compliant way.
327
328       You may also make HTTP requests using custom verbs (beyond "GET",
329       "POST", "PUT", etc.) by building your own transaction object. See
330       "Custom transactions" below.
331
332       Using HTTP request assertions
333
334       To post a URL-encoded form to the "/calls" endpoint of an application,
335       we simply use the "form" content type shortcut:
336
337         $t->post_ok('/calls' => form => {to => '+43.55.555.5555'});
338
339       Which will create the following HTTP request:
340
341         POST /calls HTTP/1.1
342         Content-Length: 20
343         Content-Type: application/x-www-form-urlencoded
344
345         to=%2B43.55.555.5555
346
347       The *_ok HTTP request assertion methods accept the same arguments as
348       their corresponding Mojo::UserAgent methods (except for the callback
349       argument).  This allows us to set headers and build query strings for
350       authentic test situations:
351
352         $t->get_ok('/internal/personnel' => {Authorization => 'Token secret-password'}
353           => form => {q => 'Professor Plum'});
354
355       which generates the following request:
356
357         GET /internal/personnel?q=Professor+Plum HTTP/1.1
358         Content-Length: 0
359         Authorization: Token secret-password
360
361       The "form" content generator (see Mojo::UserAgent::Transactor) will
362       generate a query string for "GET" requests and
363       "application/x-www-form-urlencoded" or "multipart/form-data" for POST
364       requests.
365
366       While these *_ok assertions make the HTTP requests we expect, they tell
367       us little about how well the application handled the request. The
368       application we're testing might have returned any content-type, body,
369       or HTTP status code (200, 302, 400, 404, 500, etc.) and we wouldn't
370       know it.
371
372       Test::Mojo provides assertions to test almost every aspect of the HTTP
373       response, including the HTTP response status code, the value of the
374       "Content-Type" header, and other arbitrary HTTP header information.
375
376   HTTP response status code
377       While not technically an HTTP header, the status line is the first line
378       in an HTTP response and is followed by the response headers. Testing
379       the response status code is common in REST-based and other web
380       applications that use the HTTP status codes to broadly indicate the
381       type of response the server is returning.
382
383       Testing the status code is as simple as adding the "status_is"
384       assertion:
385
386         $t->post_ok('/doorbell' => form => {action => 'ring once'})
387           ->status_is(200);
388
389       Along with "status_isnt", this will cover most needs. For more
390       elaborate status code testing, you can access the response internals
391       directly:
392
393         $t->post_ok('/doorbell' => form => {action => 'ring once'});
394         is $t->tx->res->message, 'Moved Permanently', 'try next door';
395
396   HTTP response headers
397       Test::Mojo allows us to inspect and make assertions about HTTP response
398       headers. The "Content-Type" header is commonly tested and has its own
399       assertion:
400
401         $t->get_ok('/map-of-the-world.pdf')
402           ->content_type_is('application/pdf');
403
404       This is equivalent to the more verbose:
405
406         $t->get_ok('/map-of-the-world.pdf')
407           ->header_is('Content-Type' => 'application/pdf');
408
409       We can test for multiple headers in a single response using method
410       chains:
411
412         $t->get_ok('/map-of-the-world.pdf')
413           ->content_type_is('application/pdf')
414           ->header_isnt('Compression' => 'gzip')
415           ->header_unlike('Server' => qr/IIS/i);
416
417   HTTP response content assertions
418       Test::Mojo also exposes a rich set of assertions for testing the body
419       of a response, whether that body be HTML, plain-text, or JSON. The
420       "content_*" methods look at the body of the response as plain text (as
421       defined by the response's character set):
422
423         $t->get_ok('/scary-things/spiders.json')
424           ->content_is('{"arachnid":"brown recluse"}');
425
426       Although this is a JSON document, "content_is" treats it as if it were
427       a text document. This may be useful for situations where we're looking
428       for a particular string and not concerned with the structure of the
429       document. For example, we can do the same thing with an HTML document:
430
431         $t->get_ok('/scary-things/spiders.html')
432           ->content_like(qr{<title>All The Spiders</title>});
433
434       But because Test::Mojo has access to everything that Mojo::UserAgent
435       does, we can introspect JSON documents as well as DOM-based documents
436       (HTML, XML) with assertions that allow us to check for the existence of
437       elements as well as inspect the content of text nodes.
438
439       JSON response assertions
440
441       Test::Mojo's Mojo::UserAgent has access to a JSON parser, which allows
442       us to test to see if a JSON response contains a value at a location in
443       the document using JSON pointer syntax:
444
445         $t->get_ok('/animals/friendly.json')
446           ->json_has('/beings/jeremiah/age');
447
448       This assertion tells us that the "friendly.json" document contains a
449       value at the "/beings/jeremiah/age" JSON pointer location. We can also
450       inspect the value at JSON pointer locations:
451
452         $t->get_ok('/animals/friendly.json')
453           ->json_has('/beings/jeremiah/age')
454           ->json_is('/beings/jeremiah/age' => 42)
455           ->json_like('/beings/jeremiah/species' => qr/bullfrog/i);
456
457       JSON pointer syntax makes testing JSON responses simple and readable.
458
459       DOM response assertions
460
461       We can also inspect HTML and XML responses using the Mojo::DOM parser
462       in the user agent. Here are a few examples from the Test::Mojo
463       documentation:
464
465         $t->text_is('div.foo[x=y]' => 'Hello!');
466         $t->text_is('html head title' => 'Hello!', 'right title');
467
468       The Mojo::DOM parser uses the CSS selector syntax described in
469       Mojo::DOM::CSS, allowing us to test for values in HTML and XML
470       documents without resorting to typically verbose and inflexible DOM
471       traversal methods.
472

ADVANCED TOPICS

474       This section describes some complex (but common) testing situations
475       that Test::Mojo excels in making simple.
476
477   Redirects
478       The Mojo::UserAgent object in Test::Mojo can handle HTTP redirections
479       internally to whatever level you need. Let's say we have a web service
480       that redirects "/1" to "/2", "/2" redirects to "/3", "/3" redirects to
481       "/4", and "/4" redirects to "/5":
482
483         GET /1
484
485       returns:
486
487         302 Found
488         Location: /2
489
490       and:
491
492         GET /2
493
494       returns:
495
496         302 Found
497         Location: /3
498
499       and so forth, up to "/5":
500
501         GET /5
502
503       which returns the data we wanted:
504
505         200 OK
506
507         {"message":"this is five"}
508
509       We can tell the user agent in Test::Mojo how to deal with redirects.
510       Each test is making a request to "GET /1", but we vary the number of
511       redirects the user agent should follow with each test:
512
513         my $t = Test::Mojo->new;
514
515         $t->get_ok('/1')
516           ->header_is(location => '/2');
517
518         $t->ua->max_redirects(1);
519         $t->get_ok('/1')
520           ->header_is(location => '/3');
521
522         $t->ua->max_redirects(2);
523         $t->get_ok('/1')
524           ->header_is(location => '/4');
525
526         # Look at the previous hop
527         is $t->tx->previous->res->headers->location, '/3', 'previous redirect';
528
529         $t->ua->max_redirects(3);
530         $t->get_ok('/1')
531           ->header_is(location => '/5');
532
533         $t->ua->max_redirects(4);
534         $t->get_ok('/1')
535           ->json_is('/message' => 'this is five');
536
537       When we set "max_redirects", it stays set for the life of the test
538       object until we change it.
539
540       Test::Mojo's handling of HTTP redirects eliminates the need for making
541       many, sometimes an unknown number, of redirections to keep testing
542       precise and easy to follow (ahem).
543
544   Cookies and session management
545       We can use Test::Mojo to test applications that keep session state in
546       cookies. By default, the Mojo::UserAgent object in Test::Mojo will
547       manage session for us by saving and sending cookies automatically, just
548       like common web browsers:
549
550         use Mojo::Base -strict;
551
552         use Test::More;
553         use Test::Mojo;
554
555         my $t = Test::Mojo->new('MyApp');
556
557         # No authorization cookie
558         $t->get_ok('/')
559           ->status_is(401)
560           ->content_is('Please log in');
561
562         # Application sets an authorization cookie
563         $t->post_ok('/login' => form => {password => 'let me in'})
564           ->status_is(200)
565           ->content_is('You are logged in');
566
567         # Sends the cookie from the previous transaction
568         $t->get_ok('/')
569           ->status_is(200)
570           ->content_like(qr/You logged in at \d+/);
571
572         # Clear the cookies
573         $t->reset_session;
574
575         # No authorization cookie again
576         $t->get_ok('/')
577           ->status_is(401)
578           ->content_is('Please log in');
579
580       We can also inspect cookies in responses for special values through the
581       transaction's response (Mojo::Message::Response) object:
582
583         $t->get_ok('/');
584         like $t->tx->res->cookie('smarty'), qr/smarty=pants/, 'cookie found';
585
586   Custom transactions
587       Let's say we have an application that responds to a new HTTP verb
588       "RING" and to use it we must also pass in a secret cookie value. This
589       is not a problem. We can test the application by creating a
590       Mojo::Transaction object, setting the cookie (see
591       Mojo::Message::Request), then passing the transaction object to
592       "request_ok":
593
594         # Use custom "RING" verb
595         my $tx = $t->ua->build_tx(RING => '/doorbell');
596
597         # Set a special cookie
598         $tx->req->cookies({name => 'Secret', value => "don't tell anybody"});
599
600         # Make the request
601         $t->request_ok($tx)
602           ->status_is(200)
603           ->json_is('/status' => 'ding dong');
604
605   Testing WebSocket web services
606       While the message flow on WebSocket connections can be rather dynamic,
607       it more often than not is quite predictable, which allows this rather
608       pleasant Test::Mojo WebSocket API to be used:
609
610         use Mojo::Base -strict;
611
612         use Test::More;
613         use Test::Mojo;
614
615         # Test echo web service
616         my $t = Test::Mojo->new('EchoService');
617         $t->websocket_ok('/echo')
618           ->send_ok('Hello Mojo!')
619           ->message_ok
620           ->message_is('echo: Hello Mojo!')
621           ->finish_ok;
622
623         # Test JSON web service
624         $t->websocket_ok('/echo.json')
625           ->send_ok({json => {test => [1, 2, 3]}})
626           ->message_ok
627           ->json_message_is('/test' => [1, 2, 3])
628           ->finish_ok;
629
630         done_testing();
631
632       Because of their inherent asynchronous nature, testing WebSocket
633       communications can be tricky. The Test::Mojo WebSocket assertions
634       serialize messages via event loop primitives. This enables us to treat
635       WebSocket messages as if they were using the same request-response
636       communication pattern we're accustomed to with HTTP.
637
638       To illustrate, let's walk through these tests. In the first test, we
639       use the "websocket_ok" assertion to ensure that we can connect to our
640       application's WebSocket route at "/echo" and that it's "speaking"
641       WebSocket protocol to us.  The next "send_ok" assertion tests the
642       connection again (in case it closed, for example) and attempts to send
643       the message "Hello Mojo!". The next assertion, "message_ok", blocks
644       (using the Mojo::IOLoop singleton in the application) and waits for a
645       response from the server. The response is then compared with 'echo:
646       Hello Mojo!' in the "message_is" assertion, and finally we close and
647       test our connection status again with "finish_ok".
648
649       The second test is like the first, but now we're sending and expecting
650       JSON documents at "/echo.json". In the "send_ok" assertion we take
651       advantage of Mojo::UserAgent's JSON content generator (see
652       Mojo::UserAgent::Transactor) to marshal hash and array references into
653       JSON documents, and then send them as a WebSocket message. We wait
654       (block) for a response from the server with "message_ok". Then because
655       we're expecting a JSON document back, we can leverage "json_message_ok"
656       which parses the WebSocket response body and returns an object we can
657       access through Mojo::JSON::Pointer syntax. Then we close (and test) our
658       WebSocket connection.
659
660       Testing WebSocket servers does not get any simpler than with
661       Test::Mojo.
662
663   Extending Test::Mojo
664       If you see that you're writing a lot of test assertions that aren't
665       chainable, you may benefit from writing your own test assertions. Let's
666       say we want to test the "Location" header after a redirect. We'll
667       create a new class with Role::Tiny that implements a test assertion
668       named "location_is":
669
670         package Test::Mojo::Role::Location;
671         use Mojo::Base -role;
672
673         use Test::More;
674
675         sub location_is {
676           my ($t, $value, $desc) = @_;
677           $desc ||= "Location: $value";
678           local $Test::Builder::Level = $Test::Builder::Level + 1;
679           return $t->success(is($t->tx->res->headers->location, $value, $desc));
680         }
681
682         1;
683
684       When we make new test assertions using roles, we want to use method
685       signatures that match other *_is methods in Test::Mojo, so here we
686       accept the test object, the value to compare, and an optional
687       description.
688
689       We assign a default description value ($desc), set the Test::Builder
690       "Level" global variable one level higher (which tells Test::Builder how
691       far up the call stack to look when something fails), then we use
692       Test::More's "is" function to compare the location header with the
693       expected header value. We wrap that in the "success" attribute, which
694       records the boolean test result and propagates the Test::Mojo object
695       for method chaining.
696
697       With this new package, we're ready to compose a new test object that
698       uses the role:
699
700         my $t = Test::Mojo->with_roles('+Location')->new('MyApp');
701
702         $t->post_ok('/redirect/mojo' => json => {message => 'Mojo, here I come!'})
703           ->status_is(302)
704           ->location_is('http://mojolicious.org')
705           ->or(sub { diag 'I miss tempire.' });
706
707       In this section we've covered how to add custom test assertions to
708       Test::Mojo with roles and how to use those roles to simplify testing.
709

MORE

711       You can continue with Mojolicious::Guides now or take a look at the
712       Mojolicious wiki <http://github.com/mojolicious/mojo/wiki>, which
713       contains a lot more documentation and examples by many different
714       authors.
715

SUPPORT

717       If you have any questions the documentation might not yet answer, don't
718       hesitate to ask on the mailing list
719       <http://groups.google.com/group/mojolicious> or the official IRC
720       channel "#mojo" on "irc.freenode.net" (chat now!
721       <https://kiwiirc.com/nextclient/#irc://irc.freenode.net/mojo?nick=guest-?>).
722
723
724
725perl v5.30.1                      2020-01-30   Mojolicious::Guides::Testing(3)
Impressum