/usr/share/perl5/Mojolicious/Guides/Testing.pod is in libmojolicious-perl 7.59+dfsg-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | =encoding utf8
=head1 NAME
Mojolicious::Guides::Testing - Web Application Testing Made Easy
=head1 OVERVIEW
This document is an introduction to testing web applications with L<Test::Mojo>.
L<Test::Mojo> can be thought of as a module that provides all of the tools and
testing assertions needed to test web applications in a Perl-ish way.
While L<Test::Mojo> can be used to test any web application, it has shortcuts
designed to make testing L<Mojolicious> web applications easy and pain-free.
Please refer to the L<Test::Mojo> documentation for a complete reference to many
of the ideas and syntax introduced in this document.
A test file for a simple web application might look like:
use Mojo::Base -strict;
use Test::Mojo;
use Test::More;
# Start a Mojolicious app named "Celestial"
my $t = Test::Mojo->new('Celestial');
# Post a JSON document
$t->post_ok('/notifications' => json => {event => 'full moon'})
->status_is(201)
->json_is('/message' => 'notification created');
# Perform GET requests and look at the responses
$t->get_ok('/sunrise')
->status_is(200)
->content_like(qr/ am$/);
$t->get_ok('/sunset')
->status_is(200)
->content_like(qr/ pm$/);
# Post a URL-encoded form
$t->post_ok('/insurance' => form => {name => 'Jimmy', amount => '€3.000.000'})
->status_is(200);
# Use Test::More's like() to check the response
like $t->tx->res->dom->at('div#thanks')->text, qr/thank you/, 'thanks';
done_testing();
In the rest of this document we'll explore these concepts and others related to
L<Test::Mojo>.
=head1 CONCEPTS
Essentials every L<Mojolicious> developer should know.
=head2 L<Test::Mojo> at a glance
The L<Test::More> module bundled with Perl includes several primitive test
assertions, such as C<ok>, C<is>, C<isnt>, C<like>, C<unlike>, C<cmp_ok>, etc.
An assertion "passes" if its expression returns a true value. The assertion
method prints "ok" or "not ok" if an assertion passes or fails (respectively).
L<Test::Mojo> supplies additional test assertions organized around the web
application request/response transaction (transport, response headers, response
bodies, etc.), and WebSocket communications.
One interesting thing of note: the return value of L<Test::Mojo> object
assertions is always the test object itself, allowing us to "chain" test
assertion methods. So rather than grouping related test statements like this:
$t->get_ok('/frogs');
$t->status_is(200);
$t->content_like(qr/bullfrog/);
$t->content_like(qr/hypnotoad/);
Method chaining allows us to connect test assertions that belong together:
$t->get_ok('/frogs')
->status_is(200)
->content_like(qr/bullfrog/)
->content_like(qr/hypnotoad/);
This makes for a much more I<concise> and I<coherent> testing experience:
concise because we are not repeating the invocant for each test, and coherent
because assertions that belong to the same request are syntactically bound in
the same method chain.
Occasionally it makes sense to break up a test to perform more complex
assertions on a response. L<Test::Mojo> exposes the entire transaction object so
you can get all the data you need from a response:
$t->put_ok('/bees' => json => {type => 'worker', name => 'Karl'})
->status_is(202)
->json_has('/id');
# Pull out the id from the response
my $newbee = $t->tx->res->json('/id');
# Make a new request with data from the previous response
$t->get_ok("/bees/$newbee")
->status_is(200)
->json_is('/name' => 'Karl');
The L<Test::Mojo> object is I<stateful>. As long as we haven't started a new
transaction by invoking one of the C<*_ok> methods, the request and response
objects from the previous transaction are available in the L<Test::Mojo>
object:
# First transaction
$t->get_ok('/frogs?q=bullfrog' => {'Content-Type' => 'application/json'})
->status_is(200)
->json_like('/0/species' => qr/catesbeianus/i);
# Still first transaction
$t->content_type_is('application/json');
# Second transaction
$t->get_ok('/frogs?q=banjo' => {'Content-Type' => 'text/html'})
->status_is(200)
->content_like(qr/interioris/i);
# Still second transaction
$t->content_type_is('text/html');
This statefulness also enables L<Test::Mojo> to handle sessions, follow
redirects, and inspect past responses during a redirect.
=head2 The L<Test::Mojo> object
The L<Test::Mojo> object manages the Mojolicious application lifecycle (if a
Mojolicious application class is supplied) as well as exposes the built-in
L<Mojo::UserAgent> object. To create a bare L<Test::Mojo> object:
my $t = Test::Mojo->new;
This object initializes a L<Mojo::UserAgent> object and provides a variety of
test assertion methods for accessing a web application. For example, with this
object, we could test any running web application:
$t->get_ok('https://www.google.com/')
->status_is(200)
->content_like(qr/search/i);
You can access the user agent directly if you want to make web requests without
triggering test assertions:
my $tx = $t->ua->post(
'https://duckduckgo.com/html' => form => {q => 'hypnotoad'});
$tx->result->dom->find('a.result__a')->each(sub { say $_->text });
See L<Mojo::UserAgent> for the complete API and return values.
=head2 Testing Mojolicious applications
If you pass the name of a L<Mojolicious> application class (e.g., 'MyApp') to
the L<Test::Mojo> constructor, L<Test::Mojo> will instantiate the class and
start it, and cause it to listen on a random (unused) port number. Testing a
Mojolicious application using L<Test::Mojo> will never conflict with running
applications, including the application you're testing.
The L<Mojo::UserAgent> object in L<Test::Mojo> will know where the application
is running and make requests to it. Once the tests have completed, the
L<Mojolicious> application will be torn down.
# Listens on localhost:32114 (some unused TCP port)
my $t = Test::Mojo->new('Frogs');
This object initializes a L<Mojo::UserAgent> object, loads the Mojolicious
application C<Frogs>, binds and listens on a free TCP port (e.g., 32114), and
starts the application event loop. When the L<Test::Mojo> object (C<$t>) goes
out of scope, the application is stopped.
Relative URLs in the test object method assertions (C<get_ok>, C<post_ok>, etc.)
will be sent to the Mojolicious application started by L<Test::Mojo>:
# Rewritten to "http://localhost:32114/frogs"
$t->get_ok('/frogs');
L<Test::Mojo> has a lot of handy shortcuts built into it to make testing
L<Mojolicious> or L<Mojolicious::Lite> applications enjoyable.
=head3 An example
Let's spin up a Mojolicious application using C<mojo generate app MyApp>. The
C<mojo> utility will create a working application and a C<t> directory with a
working test file:
$ mojo generate app MyApp
[mkdir] /my_app/script
[write] /my_app/script/my_app
[chmod] /my_app/script/my_app 744
...
[mkdir] /my_app/t
[write] /my_app/t/basic.t
...
Let's run the tests (we'll create the C<log> directory to quiet the application
output):
$ cd my_app
$ mkdir log
$ prove -lv t
t/basic.t ..
ok 1 - GET /
ok 2 - 200 OK
ok 3 - content is similar
1..3
ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.33 cusr 0.07
csys = 0.44 CPU)
Result: PASS
The boilerplate test file looks like this:
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('MyApp');
$t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
done_testing();
Here we can see our application class name C<MyApp> is passed to the
L<Test::Mojo> constructor. Under the hood, L<Test::Mojo> creates a new
L<Mojo::Server> instance, loads C<MyApp> (which we just created), and runs the
application. We write our tests with relative URLs because L<Test::Mojo> takes
care of getting the request to the running test application (since its port may
change between runs).
=head3 Testing with configuration data
We can alter the behavior of our application using environment variables (such
as C<MOJO_MODE>) and through configuration values. One nice feature of
L<Test::Mojo> is its ability to pass configuration values directly from its
constructor.
Let's modify our application and add a "feature flag" to enable a new feature
when the C<enable_weather> configuration value is set:
# Load configuration from hash returned by "my_app.conf"
my $config = $self->plugin('Config');
# Normal route to controller
$r->get('/')->to('example#welcome');
# NEW: this route only exists if "enable_weather" is set in the configuration
if ($config->{enable_weather}) {
$r->get('/weather' => sub { shift->render(text => "It's hot! 🔥") }
}
To test this new feature, we don't even need to create a configuration file—we
can simply pass the configuration data to the application directly via
L<Test::Mojo>'s constructor:
my $t = Test::Mojo->new(MyApp => {enable_weather => 1});
$t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
$t->get_ok('/weather')->status_is(200)->content_like(qr/🔥/);
When we run these tests, L<Test::Mojo> will pass this configuration data to the
application, which will cause it to create a special C</weather> route that we
can access in our tests. Unless C<enable_weather> is set in a configuration
file, this route will not exist when the application runs. Feature flags like
this allow us to do soft rollouts of features, targeting a small audience for a
period of time. Once the feature has been proven, we can refactor the
conditional and make it a full release.
This example shows how easy it is to start testing a Mojolicious application and
how to set specific application configuration directives from a test file.
=head3 Testing application helpers
Let's say we register a helper in our application to generate an HTTP Basic
Authorization header:
use Mojo::Util 'b64_encode';
app->helper(basic_auth => sub {
my ($c, @values) = @_;
return {Authorization => 'Basic ' . b64_encode join(':' => @values), ''};
});
How do we test application helpers like this? L<Test::Mojo> has access to the
application object, which allows us to invoke helpers from our test file:
my $t = Test::Mojo->new('MyApp');
is_deeply $t->app->basic_auth(bif => "Bif's Passwerdd"),
{Authorization => 'Basic YmlmOkJpZidzIFBhc3N3ZXJkZA=='},
'correct header value';
Any aspect of the application (helpers, plugins, routes, etc.) can be
introspected from L<Test::Mojo> through the application object. This enables us
to get deep test coverage of L<Mojolicious>-based applications.
=head1 ASSERTIONS
This section describes the basic test assertions supplied by L<Test::Mojo>.
There are four broad categories of assertions for HTTP requests:
=over 2
=item * HTTP requests
=item * HTTP response status
=item * HTTP response headers
=item * HTTP response content/body
=back
WebSocket test assertions are covered in L</Testing WebSocket web services>.
=head2 HTTP request assertions
L<Test::Mojo> has a L<Mojo::UserAgent> object that allows it to make HTTP
requests and check for HTTP transport errors. HTTP request assertions include
C<get_ok>, C<post_ok>, etc. These assertions do not test whether the request
was handled I<successfully>, only that the web application handled the request
in an HTTP compliant way.
You may also make HTTP requests using custom verbs (beyond C<GET>, C<POST>,
C<PUT>, etc.) by building your own transaction object. See
L</"Custom transactions"> below.
=head3 Using HTTP request assertions
To post a URL-encoded form to the C</calls> endpoint of an application, we
simply use the C<form> content type shortcut:
$t->post_ok('/calls' => form => {to => '+43.55.555.5555'});
Which will create the following HTTP request:
POST /calls HTTP/1.1
Content-Length: 20
Content-Type: application/x-www-form-urlencoded
to=%2B43.55.555.5555
The C<*_ok> HTTP request assertion methods accept the same arguments as their
corresponding L<Mojo::UserAgent> methods (except for the callback argument).
This allows us to set headers and build query strings for authentic test
situations:
$t->get_ok('/internal/personnel' => {Authorization => 'Token secret-password'}
=> form => {q => 'Professor Plum'});
which generates the following request:
GET /internal/personnel?q=Professor+Plum HTTP/1.1
Content-Length: 0
Authorization: Token secret-password
The C<form> content generator (see L<Mojo::UserAgent::Transactor>) will generate
a query string for C<GET> requests and C<application/x-www-form-urlencoded> or
C<multipart/form-data> for POST requests.
While these C<*_ok> assertions make the HTTP I<requests> we expect, they tell us
little about I<how well> the application handled the request. The application
we're testing might have returned any content-type, body, or HTTP status code
(200, 302, 400, 404, 500, etc.) and we wouldn't know it.
L<Test::Mojo> provides assertions to test almost every aspect of the HTTP
response, including the HTTP response status code, the value of the
C<Content-Type> header, and other arbitrary HTTP header information.
=head2 HTTP response status code
While not technically an HTTP header, the status line is the first line in an
HTTP response and is followed by the response headers. Testing the response
status code is common in REST-based and other web applications that use the HTTP
status codes to broadly indicate the type of response the server is returning.
Testing the status code is as simple as adding the C<status_is> assertion:
$t->post_ok('/doorbell' => form => {action => 'ring once'})
->status_is(200);
Along with C<status_isnt>, this will cover most needs. For more elaborate status
code testing, you can access the response internals directly:
$t->post_ok('/doorbell' => form => {action => 'ring once'});
is $t->tx->res->message, 'Moved Permanently', 'try next door';
=head2 HTTP response headers
L<Test::Mojo> allows us to inspect and make assertions about HTTP response
headers. The C<Content-Type> header is commonly tested and has its own
assertion:
$t->get_ok('/map-of-the-world.pdf')
->content_type_is('application/pdf');
This is equivalent to the more verbose:
$t->get_ok('/map-of-the-world.pdf')
->header_is('Content-Type' => 'application/pdf');
We can test for multiple headers in a single response using method chains:
$t->get_ok('/map-of-the-world.pdf')
->content_type_is('application/pdf')
->header_isnt('Compression' => 'gzip')
->header_unlike('Server' => qr/IIS/i);
=head2 HTTP response content assertions
L<Test::Mojo> also exposes a rich set of assertions for testing the body of a
response, whether that body be HTML, plain-text, or JSON. The C<content_*>
methods look at the body of the response as plain text (as defined by the
response's character set):
$t->get_ok('/scary-things/spiders.json')
->content_is('{"arachnid":"brown recluse"}');
Although this is a JSON document, C<content_is> treats it as if it were a text
document. This may be useful for situations where we're looking for a particular
string and not concerned with the structure of the document. For example, we can
do the same thing with an HTML document:
$t->get_ok('/scary-things/spiders.html')
->content_like(qr{<title>All The Spiders</title>});
But because L<Test::Mojo> has access to everything that L<Mojo::UserAgent> does,
we can introspect JSON documents as well as DOM-based documents (HTML, XML) with
assertions that allow us to check for the existence of elements as well as
inspect the content of text nodes.
=head3 JSON response assertions
L<Test::Mojo>'s L<Mojo::UserAgent> has access to a JSON parser, which allows us
to test to see if a JSON response contains a value at a location in the document
using JSON pointer syntax:
$t->get_ok('/animals/friendly.json')
->json_has('/beings/jeremiah/age');
This assertion tells us that the C<friendly.json> document contains a value at
the C</beings/jeremiah/age> JSON pointer location. We can also inspect the value
at JSON pointer locations:
$t->get_ok('/animals/friendly.json')
->json_has('/beings/jeremiah/age')
->json_is('/beings/jeremiah/age' => 42)
->json_like('/beings/jeremiah/species' => qr/bullfrog/i);
JSON pointer syntax makes testing JSON responses simple and readable.
=head3 DOM response assertions
We can also inspect HTML and XML responses using the L<Mojo::DOM> parser in the
user agent. Here are a few examples from the L<Test::Mojo> documentation:
$t->text_is('div.foo[x=y]' => 'Hello!');
$t->text_is('html head title' => 'Hello!', 'right title');
The L<Mojo::DOM> parser uses the CSS selector syntax described in
L<Mojo::DOM::CSS>, allowing us to test for values in HTML and XML documents
without resorting to typically verbose and inflexible DOM traversal methods.
=head1 ADVANCED TOPICS
This section describes some complex (but common) testing situations that
L<Test::Mojo> excels in making simple.
=head2 Redirects
The L<Mojo::UserAgent> object in L<Test::Mojo> can handle HTTP redirections
internally to whatever level you need. Let's say we have a web service that
redirects C</1> to C</2>, C</2> redirects to C</3>, C</3> redirects to C</4>,
and C</4> redirects to C</5>:
GET /1
returns:
302 Found
Location: /2
and:
GET /2
returns:
302 Found
Location: /3
and so forth, up to C</5>:
GET /5
which returns the data we wanted:
200 OK
{"message":"this is five"}
We can tell the user agent in L<Test::Mojo> how to deal with redirects. Each
test is making a request to C<GET /1>, but we vary the number of redirects the
user agent should follow with each test:
my $t = Test::Mojo->new;
$t->get_ok('/1')
->header_is(location => '/2');
$t->ua->max_redirects(1);
$t->get_ok('/1')
->header_is(location => '/3');
$t->ua->max_redirects(2);
$t->get_ok('/1')
->header_is(location => '/4');
# Look at the previous hop
is $t->tx->previous->res->headers->location, '/3', 'previous redirect';
$t->ua->max_redirects(3);
$t->get_ok('/1')
->header_is(location => '/5');
$t->ua->max_redirects(4);
$t->get_ok('/1')
->json_is('/message' => 'this is five');
When we set C<max_redirects>, it stays set for the life of the test object until
we change it.
L<Test::Mojo>'s handling of HTTP redirects eliminates the need for making many,
sometimes an unknown number, of redirections to keep testing precise and easy to
follow (ahem).
=head2 Cookies and session management
We can use L<Test::Mojo> to test applications that keep session state in
cookies. By default, the L<Mojo::UserAgent> object in L<Test::Mojo> will manage
session for us by saving and sending cookies automatically, just like common web
browsers:
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('MyApp');
# No authorization cookie
$t->get_ok('/')
->status_is(401)
->content_is('Please log in');
# Application sets an authorization cookie
$t->post_ok('/login' => form => {password => 'let me in'})
->status_is(200)
->content_is('You are logged in');
# Sends the cookie from the previous transaction
$t->get_ok('/')
->status_is(200)
->content_like(qr/You logged in at \d+/);
# Clear the cookies
$t->reset_session;
# No authorization cookie again
$t->get_ok('/')
->status_is(401)
->content_is('Please log in');
We can also inspect cookies in responses for special values through the
transaction's response (L<Mojo::Message::Response>) object:
$t->get_ok('/');
like $t->tx->res->cookie('smarty'), qr/smarty=pants/, 'cookie found';
=head2 Custom transactions
Let's say we have an application that responds to a new HTTP verb C<RING> and to
use it we must also pass in a secret cookie value. This is not a problem. We can
test the application by creating a L<Mojo::Transaction> object, setting the
cookie (see L<Mojo::Message::Request>), then passing the transaction object to
C<request_ok>:
# Use custom "RING" verb
my $tx = $t->ua->build_tx(RING => '/doorbell');
# Set a special cookie
$tx->req->cookies({name => 'Secret', value => "don't tell anybody"});
# Make the request
$t->request_ok($tx)
->status_is(200)
->json_is('/status' => 'ding dong');
=head2 Testing WebSocket web services
While the message flow on WebSocket connections can be rather dynamic, it more
often than not is quite predictable, which allows this rather pleasant
L<Test::Mojo> WebSocket API to be used:
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
# Test echo web service
my $t = Test::Mojo->new('EchoService');
$t->websocket_ok('/echo')
->send_ok('Hello Mojo!')
->message_ok
->message_is('echo: Hello Mojo!')
->finish_ok;
# Test JSON web service
$t->websocket_ok('/echo.json')
->send_ok({json => {test => [1, 2, 3]}})
->message_ok
->json_message_is('/test' => [1, 2, 3])
->finish_ok;
done_testing();
Because of their inherent asynchronous nature, testing WebSocket communications
can be tricky. The L<Test::Mojo> WebSocket assertions serialize messages via
event loop primitives. This enables us to treat WebSocket messages as if they
were using the same request-response communication pattern we're accustomed to
with HTTP.
To illustrate, let's walk through these tests. In the first test, we use the
C<websocket_ok> assertion to ensure that we can connect to our application's
WebSocket route at C</echo> and that it's "speaking" WebSocket protocol to us.
The next C<send_ok> assertion tests the connection again (in case it closed, for
example) and attempts to send the message C<Hello Mojo!>. The next assertion,
C<message_ok>, blocks (using the L<Mojo::IOLoop> singleton in the application)
and waits for a response from the server. The response is then compared with
C<'echo: Hello Mojo!'> in the C<message_is> assertion, and finally we close and
test our connection status again with C<finish_ok>.
The second test is like the first, but now we're sending and expecting JSON
documents at C</echo.json>. In the C<send_ok> assertion we take advantage of
L<Mojo::UserAgent>'s JSON content generator (see L<Mojo::UserAgent::Transactor>)
to marshal hash and array references into JSON documents, and then send them as
a WebSocket message. We wait (block) for a response from the server with
C<message_ok>. Then because we're expecting a JSON document back, we can
leverage C<json_message_ok> which parses the WebSocket response body and returns
an object we can access through L<Mojo::JSON::Pointer> syntax. Then we close
(and test) our WebSocket connection.
Testing WebSocket servers does not get any simpler than with L<Test::Mojo>.
=head2 Extending L<Test::Mojo>
If you see that you're writing a lot of test assertions that aren't chainable,
you may benefit from writing your own test assertions. Let's say we want to test
the C<Location> header after a redirect. We'll create a new class with
L<Role::Tiny> that implements a test assertion named C<location_is>:
package Test::Mojo::Role::Location;
use Mojo::Base -role;
use Test::More;
sub location_is {
my ($t, $value, $desc) = @_;
$desc ||= "Location: $value";
local $Test::Builder::Level = $Test::Builder::Level + 1;
return $t->success(is($t->tx->res->headers->location, $value, $desc));
}
1;
When we make new test assertions using roles, we want to use method signatures
that match other C<*_is> methods in L<Test::Mojo>, so here we accept the test
object, the value to compare, and an optional description.
We assign a default description value (C<$desc>), set the L<Test::Builder>
C<Level> global variable one level higher (which tells L<Test::Builder> how far
up the call stack to look when something fails), then we use L<Test::More>'s
C<is> function to compare the location header with the expected header value. We
wrap that in the C<success> attribute, which records the boolean test result and
propagates the L<Test::Mojo> object for method chaining.
With this new package, we're ready to compose a new test object that uses the
role:
my $t = Test::Mojo->with_roles('+Location')->new('MyApp');
$t->post_ok('/redirect/mojo' => json => {message => 'Mojo, here I come!'})
->status_is(302)
->location_is('http://mojolicious.org')
->or(sub { diag 'I miss tempire.' });
In this section we've covered how to add custom test assertions to L<Test::Mojo>
with roles and how to use those roles to simplify testing.
=head1 MORE
You can continue with L<Mojolicious::Guides> now or take a look at the
L<Mojolicious wiki|http://github.com/kraih/mojo/wiki>, which contains a lot more
documentation and examples by many different authors.
=head1 SUPPORT
If you have any questions the documentation might not yet answer, don't
hesitate to ask on the
L<mailing list|http://groups.google.com/group/mojolicious> or the official IRC
channel C<#mojo> on C<irc.perl.org>
(L<chat now!|https://chat.mibbit.com/?channel=%23mojo&server=irc.perl.org>).
=cut
|