CINXE.COM
Test::Mojo - Testing Mojo
<!doctype html><html> <head> <link rel="apple-touch-icon" href="/mojolicious/touch-icon.png"> <link rel="apple-touch-icon" sizes="152x152" href="/mojolicious/touch-icon-152x152.png"> <link rel="apple-touch-icon" sizes="167x167" href="/mojolicious/touch-icon-167x167.png"> <link rel="apple-touch-icon" sizes="180x180" href="/mojolicious/touch-icon-180x180.png"> <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Mojolicious" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Test::Mojo - Testing Mojo</title> <script src="/mojolicious/jquery/jquery.js"></script> <script src="/mojolicious/highlight.js/highlight.min.js"></script> <link href="/mojolicious/highlight.js/highlight-mojo-light.css" rel="stylesheet"> <script>hljs.initHighlightingOnLoad();</script> <script src="/mojolicious/bootstrap/bootstrap.js"></script> <link href="/mojolicious/bootstrap/bootstrap.css" rel="stylesheet"> <link href="/mojolicious/fontawesome/fontawesome.css" rel="stylesheet"> <link href="/app.css?v=3" rel="stylesheet"> <link href="/mojodocs.css" rel="stylesheet"> </head> <body><a id="toc"></a> <header> <nav class="navbar navbar-expand-lg navbar-dark mojobar"> <a href="https://mojolicious.org" id="mojobar-brand" class="navbar-brand"> <picture> <img src="/mojo/logo-white.png" srcset="/mojo/logo-white-2x.png 2x"> </picture> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div id="navbarNav" class="collapse navbar-collapse"> <ul class="navbar-nav mr-auto"> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Documentation </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="https://docs.mojolicious.org">Overview</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Tutorial">Tutorial</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Growing">Growing</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Routing">Routing</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Rendering">Rendering</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Testing">Testing</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Cookbook">Cookbook</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/Contributing">Contributing</a> <a class="dropdown-item" href="https://docs.mojolicious.org/Mojolicious/Guides/FAQ">FAQ</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="https://docs.mojolicious.org#API">API</a> </div> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="communityDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Community </a> <div class="dropdown-menu" aria-labelledby="communityDropdown"> <a class="dropdown-item" href="https://web.libera.chat/#mojo">IRC</a> <a class="dropdown-item" href="https://forum.mojolicious.org">Forum</a> <a class="dropdown-item" href="https://fosstodon.org/@mojolicious">Mastodon</a> <a class="dropdown-item" href="https://www.linkedin.com/groups/8963713/">LinkedIn</a> <a class="dropdown-item" href="https://github.com/mojolicious/mojo/wiki">Wiki</a> <a class="dropdown-item" href="https://metacpan.org/release/Mojolicious/">CPAN</a> </div> </li> <li class="nav-item"> <a class="nav-link" href="https://github.com/mojolicious/mojo/">Contribute on GitHub</a> </li> </ul> <a class="navbar-brand" href="https://mojojs.org"> <picture> <img src="/mojolicious/mojojs-white.png" srcset="/mojolicious/mojojs-white-2x.png 2x"> </picture> </a> <form action="https://www.google.com/cse" target="_blank" class="form-inline my-2 my-lg-0"> <input name="cx" type="hidden" value="014527573091551588235:pwfplkjpgbi"> <input name="ie" type="hidden" value="UTF-8"> <input name="q" placeholder="Search..." type="search"> </form> </div> </nav> </header> <div class="container-fluid"> <div class="row flex-wrap"> <aside class="col-sm-12 col-md-4 col-lg-2 mojo-sidebar"> <form class="mojo-version d-flex align-items-center"> <fieldset disabled> <input type="text" id="disabledTextInput" class="form-control" placeholder="v9.40"> </fieldset> </form> <ul> <li> <a href="#NAME"><b>NAME</b></a> </li> <li> <a href="#SYNOPSIS"><b>SYNOPSIS</b></a> </li> <li> <a href="#DESCRIPTION"><b>DESCRIPTION</b></a> </li> <li> <a href="#ATTRIBUTES"><b>ATTRIBUTES</b></a> <ul> <li><a href="#handler">handler</a></li> <li><a href="#message">message</a></li> <li><a href="#success">success</a></li> <li><a href="#tx">tx</a></li> <li><a href="#ua">ua</a></li> </ul> </li> <li> <a href="#METHODS"><b>METHODS</b></a> <ul> <li><a href="#app">app</a></li> <li><a href="#attr_is">attr_is</a></li> <li><a href="#attr_isnt">attr_isnt</a></li> <li><a href="#attr_like">attr_like</a></li> <li><a href="#attr_unlike">attr_unlike</a></li> <li><a href="#content_is">content_is</a></li> <li><a href="#content_isnt">content_isnt</a></li> <li><a href="#content_like">content_like</a></li> <li><a href="#content_type_is">content_type_is</a></li> <li><a href="#content_type_isnt">content_type_isnt</a></li> <li><a href="#content_type_like">content_type_like</a></li> <li><a href="#content_type_unlike">content_type_unlike</a></li> <li><a href="#content_unlike">content_unlike</a></li> <li><a href="#delete_ok">delete_ok</a></li> <li><a href="#element_count_is">element_count_is</a></li> <li><a href="#element_exists">element_exists</a></li> <li><a href="#element_exists_not">element_exists_not</a></li> <li><a href="#finish_ok">finish_ok</a></li> <li><a href="#finished_ok">finished_ok</a></li> <li><a href="#get_ok">get_ok</a></li> <li><a href="#head_ok">head_ok</a></li> <li><a href="#header_exists">header_exists</a></li> <li><a href="#header_exists_not">header_exists_not</a></li> <li><a href="#header_is">header_is</a></li> <li><a href="#header_isnt">header_isnt</a></li> <li><a href="#header_like">header_like</a></li> <li><a href="#header_unlike">header_unlike</a></li> <li><a href="#json_has">json_has</a></li> <li><a href="#json_hasnt">json_hasnt</a></li> <li><a href="#json_is">json_is</a></li> <li><a href="#json_like">json_like</a></li> <li><a href="#json_message_has">json_message_has</a></li> <li><a href="#json_message_hasnt">json_message_hasnt</a></li> <li><a href="#json_message_is">json_message_is</a></li> <li><a href="#json_message_like">json_message_like</a></li> <li><a href="#json_message_unlike">json_message_unlike</a></li> <li><a href="#json_unlike">json_unlike</a></li> <li><a href="#message_is">message_is</a></li> <li><a href="#message_isnt">message_isnt</a></li> <li><a href="#message_like">message_like</a></li> <li><a href="#message_ok">message_ok</a></li> <li><a href="#message_unlike">message_unlike</a></li> <li><a href="#new">new</a></li> <li><a href="#options_ok">options_ok</a></li> <li><a href="#or">or</a></li> <li><a href="#patch_ok">patch_ok</a></li> <li><a href="#post_ok">post_ok</a></li> <li><a href="#put_ok">put_ok</a></li> <li><a href="#request_ok">request_ok</a></li> <li><a href="#reset_session">reset_session</a></li> <li><a href="#send_ok">send_ok</a></li> <li><a href="#status_is">status_is</a></li> <li><a href="#status_isnt">status_isnt</a></li> <li><a href="#test">test</a></li> <li><a href="#text_is">text_is</a></li> <li><a href="#text_isnt">text_isnt</a></li> <li><a href="#text_like">text_like</a></li> <li><a href="#text_unlike">text_unlike</a></li> <li><a href="#websocket_ok">websocket_ok</a></li> </ul> </li> <li> <a href="#SEE-ALSO"><b>SEE ALSO</b></a> </li> </ul> </aside> <main class="col-sm-12 col-md-8 col-lg-10 py-md-3 pl-md-5 mojo-main"> <div class="mojo-docinfo"> <div> <div class="mojo-docnav"> <a class="nav-link" alt="API" href="https://docs.mojolicious.org#API"><i class="fas fa-book"></i></a> </div> Module: <a href="http://docs.mojolicious.org/Test">Test</a>::<a href="http://docs.mojolicious.org/Test/Mojo">Mojo</a> <div class="mojo-parent"> Parent: <a href="/Mojo/Base">Mojo::Base</a> </div> </div> </div> <div class="mojo-external"> <a class="btn btn-outline-secondary btn-sm" href="http://docs.mojolicious.org/Test/Mojo.txt" role="button"> Source </a> <a class="btn btn-outline-secondary btn-sm" href="https://metacpan.org/pod/Test::Mojo" role="button">CPAN</a> </div> <div class="mojo-perldoc"> <h1 id="NAME"><a class="permalink" href="#NAME">#</a><a href="#toc">NAME</a></h1> <p>Test::Mojo - Testing Mojo</p> <h1 id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">#</a><a href="#toc">SYNOPSIS</a></h1> <pre><code>use Test::More; use Test::Mojo; my $t = Test::Mojo->new('MyApp'); # HTML/XML $t->get_ok('/welcome')->status_is(200)->text_is('div#message' => 'Hello!'); # JSON $t->post_ok('/search.json' => form => {q => 'Perl'}) ->status_is(200) ->header_is('Server' => 'Mojolicious (Perl)') ->header_isnt('X-Bender' => 'Bite my shiny metal ass!') ->json_is('/results/4/title' => 'Perl rocks!') ->json_like('/results/7/title' => qr/Perl/); # WebSocket $t->websocket_ok('/echo') ->send_ok('hello') ->message_ok ->message_is('echo: hello') ->finish_ok; done_testing();</code></pre> <h1 id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">#</a><a href="#toc">DESCRIPTION</a></h1> <p><a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> is a test user agent based on <a href="http://docs.mojolicious.org/Mojo/UserAgent">Mojo::UserAgent</a>, it is usually used together with <a href="http://docs.mojolicious.org/Test/More">Test::More</a> to test <a href="http://docs.mojolicious.org/Mojolicious">Mojolicious</a> applications. Just run your tests with <a href="http://docs.mojolicious.org/prove">prove</a>.</p> <pre><code class="nohighlight">$ prove -l -v $ prove -l -v t/foo.t</code></pre> <p>If it is not already defined, the <code>MOJO_LOG_LEVEL</code> environment variable will be set to <code>trace</code> or <code>fatal</code>, depending on the value of the <code>HARNESS_IS_VERBOSE</code> environment variable. And to make it esier to test HTTPS/WSS web services <a href="http://docs.mojolicious.org/Mojo/UserAgent#insecure">"insecure" in Mojo::UserAgent</a> will be activated by default for <a href="#ua">"ua"</a>.</p> <p>See <a href="http://docs.mojolicious.org/Mojolicious/Guides/Testing">Mojolicious::Guides::Testing</a> for more.</p> <h1 id="ATTRIBUTES"><a class="permalink" href="#ATTRIBUTES">#</a><a href="#toc">ATTRIBUTES</a></h1> <p><a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> implements the following attributes.</p> <h2 id="handler"><a class="permalink" href="#handler">#</a><a href="#toc">handler</a></h2> <pre><code>my $cb = $t->handler; $t = $t->handler(sub {...});</code></pre> <p>A callback to connect <a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> with <a href="http://docs.mojolicious.org/Test/More">Test::More</a>.</p> <pre><code>$t->handler(sub ($name, @args) { return Test::More->can($name)->(@args); });</code></pre> <h2 id="message"><a class="permalink" href="#message">#</a><a href="#toc">message</a></h2> <pre><code>my $msg = $t->message; $t = $t->message([text => $bytes]);</code></pre> <p>Current WebSocket message represented as an array reference containing the frame type and payload.</p> <pre><code># More specific tests use Mojo::JSON qw(decode_json); my $hash = decode_json $t->message->[1]; is ref $hash, 'HASH', 'right reference'; is $hash->{foo}, 'bar', 'right value'; # Test custom message $t->message([binary => $bytes]) ->json_message_has('/foo/bar') ->json_message_hasnt('/bar') ->json_message_is('/foo/baz' => {yada => [1, 2, 3]});</code></pre> <h2 id="success"><a class="permalink" href="#success">#</a><a href="#toc">success</a></h2> <pre><code>my $bool = $t->success; $t = $t->success($bool);</code></pre> <p>True if the last test was successful.</p> <pre><code># Build custom tests my $location_is = sub ($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)); }; $t->get_ok('/') ->status_is(302) ->$location_is('https://mojolicious.org') ->or(sub { diag 'Must have been Joel!' });</code></pre> <h2 id="tx"><a class="permalink" href="#tx">#</a><a href="#toc">tx</a></h2> <pre><code>my $tx = $t->tx; $t = $t->tx(Mojo::Transaction::HTTP->new);</code></pre> <p>Current transaction, usually a <a href="http://docs.mojolicious.org/Mojo/Transaction/HTTP">Mojo::Transaction::HTTP</a> or <a href="http://docs.mojolicious.org/Mojo/Transaction/WebSocket">Mojo::Transaction::WebSocket</a> object.</p> <pre><code># More specific tests is $t->tx->res->json->{foo}, 'bar', 'right value'; ok $t->tx->res->content->is_multipart, 'multipart content'; is $t->tx->previous->res->code, 302, 'right status';</code></pre> <h2 id="ua"><a class="permalink" href="#ua">#</a><a href="#toc">ua</a></h2> <pre><code>my $ua = $t->ua; $t = $t->ua(Mojo::UserAgent->new);</code></pre> <p>User agent used for testing, defaults to a <a href="http://docs.mojolicious.org/Mojo/UserAgent">Mojo::UserAgent</a> object.</p> <pre><code># Allow redirects $t->ua->max_redirects(10); $t->get_ok('/redirect')->status_is(200)->content_like(qr/redirected/); # Switch protocol from HTTP to HTTPS $t->ua->server->url('https'); $t->get_ok('/secure')->status_is(200)->content_like(qr/secure/); # Use absolute URL for request with Basic authentication my $url = $t->ua->server->url->userinfo('sri:secr3t')->path('/secrets.json'); $t->post_ok($url => json => {limit => 10}) ->status_is(200) ->json_is('/1/content', 'Mojo rocks!'); # Customize all transactions (including followed redirects) $t->ua->on(start => sub ($ua, $tx) { $tx->req->headers->accept_language('en-US') }); $t->get_ok('/hello')->status_is(200)->content_like(qr/Howdy/);</code></pre> <h1 id="METHODS"><a class="permalink" href="#METHODS">#</a><a href="#toc">METHODS</a></h1> <p><a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> inherits all methods from <a href="http://docs.mojolicious.org/Mojo/Base">Mojo::Base</a> and implements the following new ones.</p> <h2 id="app"><a class="permalink" href="#app">#</a><a href="#toc">app</a></h2> <pre><code>my $app = $t->app; $t = $t->app(Mojolicious->new);</code></pre> <p>Access application with <a href="http://docs.mojolicious.org/Mojo/UserAgent/Server#app">"app" in Mojo::UserAgent::Server</a>.</p> <pre><code># Change log level $t->app->log->level('fatal'); # Test application directly is $t->app->defaults->{foo}, 'bar', 'right value'; ok $t->app->routes->find('echo')->is_websocket, 'WebSocket route'; my $c = $t->app->build_controller; ok $c->render(template => 'foo'), 'rendering was successful'; is $c->res->status, 200, 'right status'; is $c->res->body, 'Foo!', 'right content'; # Change application behavior $t->app->hook(before_dispatch => sub ($c) { $c->render(text => 'This request did not reach the router.') if $c->req->url->path->contains('/user'); }); $t->get_ok('/user')->status_is(200)->content_like(qr/not reach the router/); # Extract additional information my $stash; $t->app->hook(after_dispatch => sub ($c) { $stash = $c->stash }); $t->get_ok('/hello')->status_is(200); is $stash->{foo}, 'bar', 'right value';</code></pre> <h2 id="attr_is"><a class="permalink" href="#attr_is">#</a><a href="#toc">attr_is</a></h2> <pre><code>$t = $t->attr_is('img.cat', 'alt', 'Grumpy cat'); $t = $t->attr_is('img.cat', 'alt', 'Grumpy cat', 'right alt text');</code></pre> <p>Checks text content of attribute with <a href="http://docs.mojolicious.org/Mojo/DOM#attr">"attr" in Mojo::DOM</a> at the CSS selectors first matching HTML/XML element for exact match with <a href="http://docs.mojolicious.org/Mojo/DOM#at">"at" in Mojo::DOM</a>.</p> <h2 id="attr_isnt"><a class="permalink" href="#attr_isnt">#</a><a href="#toc">attr_isnt</a></h2> <pre><code>$t = $t->attr_isnt('img.cat', 'alt', 'Calm cat'); $t = $t->attr_isnt('img.cat', 'alt', 'Calm cat', 'different alt text');</code></pre> <p>Opposite of <a href="#attr_is">"attr_is"</a>.</p> <h2 id="attr_like"><a class="permalink" href="#attr_like">#</a><a href="#toc">attr_like</a></h2> <pre><code>$t = $t->attr_like('img.cat', 'alt', qr/Grumpy/); $t = $t->attr_like('img.cat', 'alt', qr/Grumpy/, 'right alt text');</code></pre> <p>Checks text content of attribute with <a href="http://docs.mojolicious.org/Mojo/DOM#attr">"attr" in Mojo::DOM</a> at the CSS selectors first matching HTML/XML element for similar match with <a href="http://docs.mojolicious.org/Mojo/DOM#at">"at" in Mojo::DOM</a>.</p> <h2 id="attr_unlike"><a class="permalink" href="#attr_unlike">#</a><a href="#toc">attr_unlike</a></h2> <pre><code>$t = $t->attr_unlike('img.cat', 'alt', qr/Calm/); $t = $t->attr_unlike('img.cat', 'alt', qr/Calm/, 'different alt text');</code></pre> <p>Opposite of <a href="#attr_like">"attr_like"</a>.</p> <h2 id="content_is"><a class="permalink" href="#content_is">#</a><a href="#toc">content_is</a></h2> <pre><code>$t = $t->content_is('working!'); $t = $t->content_is('working!', 'right content');</code></pre> <p>Check response content for exact match after retrieving it from <a href="http://docs.mojolicious.org/Mojo/Message#text">"text" in Mojo::Message</a>.</p> <h2 id="content_isnt"><a class="permalink" href="#content_isnt">#</a><a href="#toc">content_isnt</a></h2> <pre><code>$t = $t->content_isnt('working!'); $t = $t->content_isnt('working!', 'different content');</code></pre> <p>Opposite of <a href="#content_is">"content_is"</a>.</p> <h2 id="content_like"><a class="permalink" href="#content_like">#</a><a href="#toc">content_like</a></h2> <pre><code>$t = $t->content_like(qr/working!/); $t = $t->content_like(qr/working!/, 'right content');</code></pre> <p>Check response content for similar match after retrieving it from <a href="http://docs.mojolicious.org/Mojo/Message#text">"text" in Mojo::Message</a>.</p> <h2 id="content_type_is"><a class="permalink" href="#content_type_is">#</a><a href="#toc">content_type_is</a></h2> <pre><code>$t = $t->content_type_is('text/html'); $t = $t->content_type_is('text/html', 'right content type');</code></pre> <p>Check response <code>Content-Type</code> header for exact match.</p> <h2 id="content_type_isnt"><a class="permalink" href="#content_type_isnt">#</a><a href="#toc">content_type_isnt</a></h2> <pre><code>$t = $t->content_type_isnt('text/html'); $t = $t->content_type_isnt('text/html', 'different content type');</code></pre> <p>Opposite of <a href="#content_type_is">"content_type_is"</a>.</p> <h2 id="content_type_like"><a class="permalink" href="#content_type_like">#</a><a href="#toc">content_type_like</a></h2> <pre><code>$t = $t->content_type_like(qr/text/); $t = $t->content_type_like(qr/text/, 'right content type');</code></pre> <p>Check response <code>Content-Type</code> header for similar match.</p> <h2 id="content_type_unlike"><a class="permalink" href="#content_type_unlike">#</a><a href="#toc">content_type_unlike</a></h2> <pre><code>$t = $t->content_type_unlike(qr/text/); $t = $t->content_type_unlike(qr/text/, 'different content type');</code></pre> <p>Opposite of <a href="#content_type_like">"content_type_like"</a>.</p> <h2 id="content_unlike"><a class="permalink" href="#content_unlike">#</a><a href="#toc">content_unlike</a></h2> <pre><code>$t = $t->content_unlike(qr/working!/); $t = $t->content_unlike(qr/working!/, 'different content');</code></pre> <p>Opposite of <a href="#content_like">"content_like"</a>.</p> <h2 id="delete_ok"><a class="permalink" href="#delete_ok">#</a><a href="#toc">delete_ok</a></h2> <pre><code>$t = $t->delete_ok('http://example.com/foo'); $t = $t->delete_ok('/foo'); $t = $t->delete_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->delete_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->delete_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>DELETE</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#delete">"delete" in Mojo::UserAgent</a>, except for the callback.</p> <h2 id="element_count_is"><a class="permalink" href="#element_count_is">#</a><a href="#toc">element_count_is</a></h2> <pre><code>$t = $t->element_count_is('div.foo[x=y]', 5); $t = $t->element_count_is('html body div', 30, 'thirty elements');</code></pre> <p>Checks the number of HTML/XML elements matched by the CSS selector with <a href="http://docs.mojolicious.org/Mojo/DOM#find">"find" in Mojo::DOM</a>.</p> <h2 id="element_exists"><a class="permalink" href="#element_exists">#</a><a href="#toc">element_exists</a></h2> <pre><code>$t = $t->element_exists('div.foo[x=y]'); $t = $t->element_exists('html head title', 'has a title');</code></pre> <p>Checks for existence of the CSS selectors first matching HTML/XML element with <a href="http://docs.mojolicious.org/Mojo/DOM#at">"at" in Mojo::DOM</a>.</p> <pre><code># Check attribute values $t->get_ok('/login') ->element_exists('label[for=email]') ->element_exists('input[name=email][type=text][value*="example.com"]') ->element_exists('label[for=pass]') ->element_exists('input[name=pass][type=password]') ->element_exists('input[type=submit][value]');</code></pre> <h2 id="element_exists_not"><a class="permalink" href="#element_exists_not">#</a><a href="#toc">element_exists_not</a></h2> <pre><code>$t = $t->element_exists_not('div.foo[x=y]'); $t = $t->element_exists_not('html head title', 'has no title');</code></pre> <p>Opposite of <a href="#element_exists">"element_exists"</a>.</p> <h2 id="finish_ok"><a class="permalink" href="#finish_ok">#</a><a href="#toc">finish_ok</a></h2> <pre><code>$t = $t->finish_ok; $t = $t->finish_ok(1000); $t = $t->finish_ok(1003 => 'Cannot accept data!');</code></pre> <p>Close WebSocket connection gracefully.</p> <h2 id="finished_ok"><a class="permalink" href="#finished_ok">#</a><a href="#toc">finished_ok</a></h2> <pre><code>$t = $t->finished_ok(1000);</code></pre> <p>Wait for WebSocket connection to be closed gracefully and check status.</p> <h2 id="get_ok"><a class="permalink" href="#get_ok">#</a><a href="#toc">get_ok</a></h2> <pre><code>$t = $t->get_ok('http://example.com/foo'); $t = $t->get_ok('/foo'); $t = $t->get_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->get_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->get_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>GET</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#get">"get" in Mojo::UserAgent</a>, except for the callback.</p> <pre><code># Run tests against remote host $t->get_ok('https://docs.mojolicious.org')->status_is(200); # Use relative URL for request with Basic authentication $t->get_ok('//sri:secr3t@/secrets.json') ->status_is(200) ->json_is('/1/content', 'Mojo rocks!'); # Run additional tests on the transaction $t->get_ok('/foo')->status_is(200); is $t->tx->res->dom->at('input')->val, 'whatever', 'right value';</code></pre> <h2 id="head_ok"><a class="permalink" href="#head_ok">#</a><a href="#toc">head_ok</a></h2> <pre><code>$t = $t->head_ok('http://example.com/foo'); $t = $t->head_ok('/foo'); $t = $t->head_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->head_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->head_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>HEAD</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#head">"head" in Mojo::UserAgent</a>, except for the callback.</p> <h2 id="header_exists"><a class="permalink" href="#header_exists">#</a><a href="#toc">header_exists</a></h2> <pre><code>$t = $t->header_exists('ETag'); $t = $t->header_exists('ETag', 'header exists');</code></pre> <p>Check if response header exists.</p> <h2 id="header_exists_not"><a class="permalink" href="#header_exists_not">#</a><a href="#toc">header_exists_not</a></h2> <pre><code>$t = $t->header_exists_not('ETag'); $t = $t->header_exists_not('ETag', 'header is missing');</code></pre> <p>Opposite of <a href="#header_exists">"header_exists"</a>.</p> <h2 id="header_is"><a class="permalink" href="#header_is">#</a><a href="#toc">header_is</a></h2> <pre><code>$t = $t->header_is(ETag => '"abc321"'); $t = $t->header_is(ETag => '"abc321"', 'right header');</code></pre> <p>Check response header for exact match.</p> <h2 id="header_isnt"><a class="permalink" href="#header_isnt">#</a><a href="#toc">header_isnt</a></h2> <pre><code>$t = $t->header_isnt(Etag => '"abc321"'); $t = $t->header_isnt(ETag => '"abc321"', 'different header');</code></pre> <p>Opposite of <a href="#header_is">"header_is"</a>.</p> <h2 id="header_like"><a class="permalink" href="#header_like">#</a><a href="#toc">header_like</a></h2> <pre><code>$t = $t->header_like(ETag => qr/abc/); $t = $t->header_like(ETag => qr/abc/, 'right header');</code></pre> <p>Check response header for similar match.</p> <h2 id="header_unlike"><a class="permalink" href="#header_unlike">#</a><a href="#toc">header_unlike</a></h2> <pre><code>$t = $t->header_unlike(ETag => qr/abc/); $t = $t->header_unlike(ETag => qr/abc/, 'different header');</code></pre> <p>Opposite of <a href="#header_like">"header_like"</a>.</p> <h2 id="json_has"><a class="permalink" href="#json_has">#</a><a href="#toc">json_has</a></h2> <pre><code>$t = $t->json_has('/foo'); $t = $t->json_has('/minibar', 'has a minibar');</code></pre> <p>Check if JSON response contains a value that can be identified using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a>.</p> <h2 id="json_hasnt"><a class="permalink" href="#json_hasnt">#</a><a href="#toc">json_hasnt</a></h2> <pre><code>$t = $t->json_hasnt('/foo'); $t = $t->json_hasnt('/minibar', 'no minibar');</code></pre> <p>Opposite of <a href="#json_has">"json_has"</a>.</p> <h2 id="json_is"><a class="permalink" href="#json_is">#</a><a href="#toc">json_is</a></h2> <pre><code>$t = $t->json_is({foo => [1, 2, 3]}); $t = $t->json_is('/foo' => [1, 2, 3]); $t = $t->json_is('/foo/1' => 2, 'right value');</code></pre> <p>Check the value extracted from JSON response using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a>, which defaults to the root value if it is omitted.</p> <pre><code># Use an empty JSON Pointer to test the whole JSON response with a test description $t->json_is('' => {foo => [1, 2, 3]}, 'right object');</code></pre> <h2 id="json_like"><a class="permalink" href="#json_like">#</a><a href="#toc">json_like</a></h2> <pre><code>$t = $t->json_like('/foo/1' => qr/^\d+$/); $t = $t->json_like('/foo/1' => qr/^\d+$/, 'right value');</code></pre> <p>Check the value extracted from JSON response using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a> for similar match.</p> <h2 id="json_message_has"><a class="permalink" href="#json_message_has">#</a><a href="#toc">json_message_has</a></h2> <pre><code>$t = $t->json_message_has('/foo'); $t = $t->json_message_has('/minibar', 'has a minibar');</code></pre> <p>Check if JSON WebSocket message contains a value that can be identified using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a>.</p> <h2 id="json_message_hasnt"><a class="permalink" href="#json_message_hasnt">#</a><a href="#toc">json_message_hasnt</a></h2> <pre><code>$t = $t->json_message_hasnt('/foo'); $t = $t->json_message_hasnt('/minibar', 'no minibar');</code></pre> <p>Opposite of <a href="#json_message_has">"json_message_has"</a>.</p> <h2 id="json_message_is"><a class="permalink" href="#json_message_is">#</a><a href="#toc">json_message_is</a></h2> <pre><code>$t = $t->json_message_is({foo => [1, 2, 3]}); $t = $t->json_message_is('/foo' => [1, 2, 3]); $t = $t->json_message_is('/foo/1' => 2, 'right value');</code></pre> <p>Check the value extracted from JSON WebSocket message using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a>, which defaults to the root value if it is omitted.</p> <h2 id="json_message_like"><a class="permalink" href="#json_message_like">#</a><a href="#toc">json_message_like</a></h2> <pre><code>$t = $t->json_message_like('/foo/1' => qr/^\d+$/); $t = $t->json_message_like('/foo/1' => qr/^\d+$/, 'right value');</code></pre> <p>Check the value extracted from JSON WebSocket message using the given JSON Pointer with <a href="http://docs.mojolicious.org/Mojo/JSON/Pointer">Mojo::JSON::Pointer</a> for similar match.</p> <h2 id="json_message_unlike"><a class="permalink" href="#json_message_unlike">#</a><a href="#toc">json_message_unlike</a></h2> <pre><code>$t = $t->json_message_unlike('/foo/1' => qr/^\d+$/); $t = $t->json_message_unlike('/foo/1' => qr/^\d+$/, 'different value');</code></pre> <p>Opposite of <a href="#json_message_like">"json_message_like"</a>.</p> <h2 id="json_unlike"><a class="permalink" href="#json_unlike">#</a><a href="#toc">json_unlike</a></h2> <pre><code>$t = $t->json_unlike('/foo/1' => qr/^\d+$/); $t = $t->json_unlike('/foo/1' => qr/^\d+$/, 'different value');</code></pre> <p>Opposite of <a href="#json_like">"json_like"</a>.</p> <h2 id="message_is"><a class="permalink" href="#message_is">#</a><a href="#toc">message_is</a></h2> <pre><code>$t = $t->message_is({binary => $bytes}); $t = $t->message_is({text => $bytes}); $t = $t->message_is('working!'); $t = $t->message_is('working!', 'right message');</code></pre> <p>Check WebSocket message for exact match.</p> <h2 id="message_isnt"><a class="permalink" href="#message_isnt">#</a><a href="#toc">message_isnt</a></h2> <pre><code>$t = $t->message_isnt({binary => $bytes}); $t = $t->message_isnt({text => $bytes}); $t = $t->message_isnt('working!'); $t = $t->message_isnt('working!', 'different message');</code></pre> <p>Opposite of <a href="#message_is">"message_is"</a>.</p> <h2 id="message_like"><a class="permalink" href="#message_like">#</a><a href="#toc">message_like</a></h2> <pre><code>$t = $t->message_like({binary => qr/$bytes/}); $t = $t->message_like({text => qr/$bytes/}); $t = $t->message_like(qr/working!/); $t = $t->message_like(qr/working!/, 'right message');</code></pre> <p>Check WebSocket message for similar match.</p> <h2 id="message_ok"><a class="permalink" href="#message_ok">#</a><a href="#toc">message_ok</a></h2> <pre><code>$t = $t->message_ok; $t = $t->message_ok('got a message');</code></pre> <p>Wait for next WebSocket message to arrive.</p> <pre><code># Wait for message and perform multiple tests on it $t->websocket_ok('/time') ->message_ok ->message_like(qr/\d+/) ->message_unlike(qr/\w+/) ->finish_ok;</code></pre> <h2 id="message_unlike"><a class="permalink" href="#message_unlike">#</a><a href="#toc">message_unlike</a></h2> <pre><code>$t = $t->message_unlike({binary => qr/$bytes/}); $t = $t->message_unlike({text => qr/$bytes/}); $t = $t->message_unlike(qr/working!/); $t = $t->message_unlike(qr/working!/, 'different message');</code></pre> <p>Opposite of <a href="#message_like">"message_like"</a>.</p> <h2 id="new"><a class="permalink" href="#new">#</a><a href="#toc">new</a></h2> <pre><code>my $t = Test::Mojo->new; my $t = Test::Mojo->new('MyApp'); my $t = Test::Mojo->new('MyApp', {foo => 'bar'}); my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl')); my $t = Test::Mojo->new(Mojo::File->new('/path/to/myapp.pl'), {foo => 'bar'}); my $t = Test::Mojo->new(MyApp->new); my $t = Test::Mojo->new(MyApp->new, {foo => 'bar'});</code></pre> <p>Construct a new <a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> object. In addition to a class name or <a href="http://docs.mojolicious.org/Mojo/File">Mojo::File</a> object pointing to the application script, you can pass along a hash reference with configuration values that will be used to override the application configuration. The special configuration value <code>config_override</code> will be set in <a href="http://docs.mojolicious.org/Mojolicious#config">"config" in Mojolicious</a> as well, which is used to disable configuration plugins like <a href="http://docs.mojolicious.org/Mojolicious/Plugin/Config">Mojolicious::Plugin::Config</a>, <a href="http://docs.mojolicious.org/Mojolicious/Plugin/JSONConfig">Mojolicious::Plugin::JSONConfig</a> and <a href="http://docs.mojolicious.org/Mojolicious/Plugin/NotYAMLConfig">Mojolicious::Plugin::NotYAMLConfig</a> for tests.</p> <pre><code># Load application script relative to the "t" directory use Mojo::File qw(curfile); my $t = Test::Mojo->new(curfile->dirname->sibling('myapp.pl'));</code></pre> <h2 id="options_ok"><a class="permalink" href="#options_ok">#</a><a href="#toc">options_ok</a></h2> <pre><code>$t = $t->options_ok('http://example.com/foo'); $t = $t->options_ok('/foo'); $t = $t->options_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->options_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->options_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>OPTIONS</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#options">"options" in Mojo::UserAgent</a>, except for the callback.</p> <h2 id="or"><a class="permalink" href="#or">#</a><a href="#toc">or</a></h2> <pre><code>$t = $t->or(sub {...});</code></pre> <p>Execute callback if the value of <a href="#success">"success"</a> is false.</p> <pre><code># Diagnostics $t->get_ok('/bad')->or(sub { diag 'Must have been Glen!' }) ->status_is(200)->or(sub { diag $t->tx->res->dom->at('title')->text });</code></pre> <h2 id="patch_ok"><a class="permalink" href="#patch_ok">#</a><a href="#toc">patch_ok</a></h2> <pre><code>$t = $t->patch_ok('http://example.com/foo'); $t = $t->patch_ok('/foo'); $t = $t->patch_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->patch_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->patch_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>PATCH</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#patch">"patch" in Mojo::UserAgent</a>, except for the callback.</p> <h2 id="post_ok"><a class="permalink" href="#post_ok">#</a><a href="#toc">post_ok</a></h2> <pre><code>$t = $t->post_ok('http://example.com/foo'); $t = $t->post_ok('/foo'); $t = $t->post_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->post_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->post_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>POST</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#post">"post" in Mojo::UserAgent</a>, except for the callback.</p> <pre><code># Test file upload my $upload = {foo => {content => 'bar', filename => 'baz.txt'}}; $t->post_ok('/upload' => form => $upload)->status_is(200); # Test JSON API $t->post_ok('/hello.json' => json => {hello => 'world'}) ->status_is(200) ->json_is({bye => 'world'});</code></pre> <h2 id="put_ok"><a class="permalink" href="#put_ok">#</a><a href="#toc">put_ok</a></h2> <pre><code>$t = $t->put_ok('http://example.com/foo'); $t = $t->put_ok('/foo'); $t = $t->put_ok('/foo' => {Accept => '*/*'} => 'Content!'); $t = $t->put_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'}); $t = $t->put_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});</code></pre> <p>Perform a <code>PUT</code> request and check for transport errors, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#put">"put" in Mojo::UserAgent</a>, except for the callback.</p> <h2 id="request_ok"><a class="permalink" href="#request_ok">#</a><a href="#toc">request_ok</a></h2> <pre><code>$t = $t->request_ok(Mojo::Transaction::HTTP->new);</code></pre> <p>Perform request and check for transport errors.</p> <pre><code># Request with custom method my $tx = $t->ua->build_tx(FOO => '/test.json' => json => {foo => 1}); $t->request_ok($tx)->status_is(200)->json_is({success => 1}); # Request with custom cookie my $tx = $t->ua->build_tx(GET => '/account'); $tx->req->cookies({name => 'user', value => 'sri'}); $t->request_ok($tx)->status_is(200)->text_is('head > title' => 'Hello sri'); # Custom WebSocket handshake my $tx = $t->ua->build_websocket_tx('/foo'); $tx->req->headers->remove('User-Agent'); $t->request_ok($tx)->message_ok->message_is('bar')->finish_ok;</code></pre> <h2 id="reset_session"><a class="permalink" href="#reset_session">#</a><a href="#toc">reset_session</a></h2> <pre><code>$t = $t->reset_session;</code></pre> <p>Reset user agent session.</p> <h2 id="send_ok"><a class="permalink" href="#send_ok">#</a><a href="#toc">send_ok</a></h2> <pre><code>$t = $t->send_ok({binary => $bytes}); $t = $t->send_ok({text => $bytes}); $t = $t->send_ok({json => {test => [1, 2, 3]}}); $t = $t->send_ok([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]); $t = $t->send_ok($chars); $t = $t->send_ok($chars, 'sent successfully');</code></pre> <p>Send message or frame via WebSocket.</p> <pre><code># Send JSON object as "Text" message $t->websocket_ok('/echo.json') ->send_ok({json => {test => 'I ♥ Mojolicious!'}}) ->message_ok ->json_message_is('/test' => 'I ♥ Mojolicious!') ->finish_ok;</code></pre> <h2 id="status_is"><a class="permalink" href="#status_is">#</a><a href="#toc">status_is</a></h2> <pre><code>$t = $t->status_is(200); $t = $t->status_is(200, 'right status');</code></pre> <p>Check response status for exact match.</p> <h2 id="status_isnt"><a class="permalink" href="#status_isnt">#</a><a href="#toc">status_isnt</a></h2> <pre><code>$t = $t->status_isnt(200); $t = $t->status_isnt(200, 'different status');</code></pre> <p>Opposite of <a href="#status_is">"status_is"</a>.</p> <h2 id="test"><a class="permalink" href="#test">#</a><a href="#toc">test</a></h2> <pre><code>$t = $t->test('is', 'first value', 'second value', 'right value');</code></pre> <p>Call <a href="http://docs.mojolicious.org/Test/More">Test::More</a> functions through <a href="#handler">"handler"</a>, used to implement <a href="http://docs.mojolicious.org/Test/Mojo">Test::Mojo</a> roles. The result will be stored in <a href="#success">"success"</a>.</p> <h2 id="text_is"><a class="permalink" href="#text_is">#</a><a href="#toc">text_is</a></h2> <pre><code>$t = $t->text_is('div.foo[x=y]' => 'Hello!'); $t = $t->text_is('html head title' => 'Hello!', 'right title');</code></pre> <p>Checks text content of the CSS selectors first matching HTML/XML element for exact match with <a href="http://docs.mojolicious.org/Mojo/DOM#at">"at" in Mojo::DOM</a>.</p> <h2 id="text_isnt"><a class="permalink" href="#text_isnt">#</a><a href="#toc">text_isnt</a></h2> <pre><code>$t = $t->text_isnt('div.foo[x=y]' => 'Hello!'); $t = $t->text_isnt('html head title' => 'Hello!', 'different title');</code></pre> <p>Opposite of <a href="#text_is">"text_is"</a>.</p> <h2 id="text_like"><a class="permalink" href="#text_like">#</a><a href="#toc">text_like</a></h2> <pre><code>$t = $t->text_like('div.foo[x=y]' => qr/Hello/); $t = $t->text_like('html head title' => qr/Hello/, 'right title');</code></pre> <p>Checks text content of the CSS selectors first matching HTML/XML element for similar match with <a href="http://docs.mojolicious.org/Mojo/DOM#at">"at" in Mojo::DOM</a>.</p> <h2 id="text_unlike"><a class="permalink" href="#text_unlike">#</a><a href="#toc">text_unlike</a></h2> <pre><code>$t = $t->text_unlike('div.foo[x=y]' => qr/Hello/); $t = $t->text_unlike('html head title' => qr/Hello/, 'different title');</code></pre> <p>Opposite of <a href="#text_like">"text_like"</a>.</p> <h2 id="websocket_ok"><a class="permalink" href="#websocket_ok">#</a><a href="#toc">websocket_ok</a></h2> <pre><code>$t = $t->websocket_ok('http://example.com/echo'); $t = $t->websocket_ok('/echo'); $t = $t->websocket_ok('/echo' => {DNT => 1} => ['v1.proto']);</code></pre> <p>Open a WebSocket connection with transparent handshake, takes the same arguments as <a href="http://docs.mojolicious.org/Mojo/UserAgent#websocket">"websocket" in Mojo::UserAgent</a>, except for the callback.</p> <pre><code># WebSocket with permessage-deflate compression $t->websocket_ok('/' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'}) ->send_ok('y' x 50000) ->message_ok ->message_is('z' x 50000) ->finish_ok;</code></pre> <h1 id="SEE-ALSO"><a class="permalink" href="#SEE-ALSO">#</a><a href="#toc">SEE ALSO</a></h1> <p><a href="http://docs.mojolicious.org/Mojolicious">Mojolicious</a>, <a href="http://docs.mojolicious.org/Mojolicious/Guides">Mojolicious::Guides</a>, <a href="https://mojolicious.org">https://mojolicious.org</a>.</p> </div> </main> </div> </div> <footer> <div class="container-fluid p-3 mojo-footer"> <div class="row"> <div class="col-sm align-self-center text-center mojo-free"> <b>Free</b> and <b>Open Source</b>. </div> <div class="col-sm align-self-center text-center mojo-copy"> <i class="far fa-copyright"></i> 2008-2023 Sebastian Riedel and the <a href="https://docs.mojolicious.org/Mojolicious#AUTHORS">Mojolicious contributors</a>. </div> <div class="col-sm align-self-center text-center mojo-social"> <a alt="GitHub" href="https://github.com/mojolicious/mojo"><i class="fab fa-github-alt"></i></a> <a alt="Mastodon" rel="me" href="https://fosstodon.org/@mojolicious"><i class="fab fa-mastodon"></i></a> <a alt="LinkedIn" href="https://www.linkedin.com/groups/8963713/"><i class="fab fa-linkedin"></i></a> </div> </div> </div> </footer> <script> const links = document.querySelectorAll('.mojo-sidebar ul li a[href^="#"]'); const linkById = Object.fromEntries(Array.from(links).map(e => [e.getAttribute('href').substr(1), e])); const sections = Array.from(links).map(e => document.getElementById(e.getAttribute('href').substr(1))).reverse(); let lastSectionId; let mojoOnScroll = () => { const pos = (document.documentElement.scrollTop || document.body.scrollTop) - 63; const section = sections.find(e => e.offsetTop <= pos); if (!section) return; if (section.id == lastSectionId) return; lastSectionId = section.id; links.forEach(e => e.classList.remove('font-weight-bold')); linkById[section.id].classList.add('font-weight-bold'); }; window.onscroll = mojoOnScroll; mojoOnScroll(); </script> </body> </html>