PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Mojolicious/Guides/Testing.pod

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