NAME
Test::Mojo - Testing Mojo
SYNOPSIS
my $t = Test::Mojo->new( 'MyApp' );
$t ->get_ok( '/welcome' )->status_is(200)->text_is( 'div#message' => 'Hello!' );
$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/ );
$t ->websocket_ok( '/echo' )
->send_ok( 'hello' )
->message_ok
->message_is( 'echo: hello' )
->finish_ok;
done_testing();
|
DESCRIPTION
Test::Mojo is a test user agent based on Mojo::UserAgent, it is usually used together with Test::More to test Mojolicious applications. Just run your tests with prove.
$ prove -l -v
$ prove -l -v t/foo.t
|
If it is not already defined, the MOJO_LOG_LEVEL
environment variable will be set to trace
or fatal
, depending on the value of the HARNESS_IS_VERBOSE
environment variable. And to make it esier to test HTTPS/WSS web services "insecure" in Mojo::UserAgent will be activated by default for "ua".
See Mojolicious::Guides::Testing for more.
ATTRIBUTES
Test::Mojo implements the following attributes.
handler
my $cb = $t ->handler;
$t = $t ->handler( sub {...});
|
A callback to connect Test::Mojo with Test::More.
$t ->handler( sub ( $name , @args ) {
return Test::More->can( $name )->( @args );
});
|
message
my $msg = $t ->message;
$t = $t ->message([ text => $bytes ]);
|
Current WebSocket message represented as an array reference containing the frame type and payload.
my $hash = decode_json $t ->message->[1];
is ref $hash , 'HASH' , 'right reference' ;
is $hash ->{foo}, 'bar' , 'right value' ;
$t ->message([ binary => $bytes ])
->json_message_has( '/foo/bar' )
->json_message_hasnt( '/bar' )
->json_message_is( '/foo/baz' => { yada => [1, 2, 3]});
|
success
my $bool = $t ->success;
$t = $t ->success( $bool );
|
True if the last test was successful.
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)
->or( sub { diag 'Must have been Joel!' });
|
tx
my $tx = $t ->tx;
$t = $t ->tx(Mojo::Transaction::HTTP->new);
|
Current transaction, usually a Mojo::Transaction::HTTP or Mojo::Transaction::WebSocket object.
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' ;
|
ua
my $ua = $t ->ua;
$t = $t ->ua(Mojo::UserAgent->new);
|
User agent used for testing, defaults to a Mojo::UserAgent object.
$t ->ua->max_redirects(10);
$t ->get_ok( '/redirect' )->status_is(200)->content_like( qr/redirected/ );
$t ->ua->server->url( 'https' );
$t ->get_ok( '/secure' )->status_is(200)->content_like( qr/secure/ );
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!' );
$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/ );
|
METHODS
Test::Mojo inherits all methods from Mojo::Base and implements the following new ones.
app
my $app = $t ->app;
$t = $t ->app(Mojolicious->new);
|
Access application with "app" in Mojo::UserAgent::Server.
$t ->app-> log ->level( 'fatal' );
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' ;
$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/ );
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' ;
|
attr_is
$t = $t ->attr_is( 'img.cat' , 'alt' , 'Grumpy cat' );
$t = $t ->attr_is( 'img.cat' , 'alt' , 'Grumpy cat' , 'right alt text' );
|
Checks text content of attribute with "attr" in Mojo::DOM at the CSS selectors first matching HTML/XML element for exact match with "at" in Mojo::DOM.
attr_isnt
$t = $t ->attr_isnt( 'img.cat' , 'alt' , 'Calm cat' );
$t = $t ->attr_isnt( 'img.cat' , 'alt' , 'Calm cat' , 'different alt text' );
|
Opposite of "attr_is".
attr_like
$t = $t ->attr_like( 'img.cat' , 'alt' , qr/Grumpy/ );
$t = $t ->attr_like( 'img.cat' , 'alt' , qr/Grumpy/ , 'right alt text' );
|
Checks text content of attribute with "attr" in Mojo::DOM at the CSS selectors first matching HTML/XML element for similar match with "at" in Mojo::DOM.
attr_unlike
$t = $t ->attr_unlike( 'img.cat' , 'alt' , qr/Calm/ );
$t = $t ->attr_unlike( 'img.cat' , 'alt' , qr/Calm/ , 'different alt text' );
|
Opposite of "attr_like".
content_is
$t = $t ->content_is( 'working!' );
$t = $t ->content_is( 'working!' , 'right content' );
|
Check response content for exact match after retrieving it from "text" in Mojo::Message.
content_isnt
$t = $t ->content_isnt( 'working!' );
$t = $t ->content_isnt( 'working!' , 'different content' );
|
Opposite of "content_is".
content_like
$t = $t ->content_like( qr/working!/ );
$t = $t ->content_like( qr/working!/ , 'right content' );
|
Check response content for similar match after retrieving it from "text" in Mojo::Message.
content_type_is
$t = $t ->content_type_is( 'text/html' );
$t = $t ->content_type_is( 'text/html' , 'right content type' );
|
Check response Content-Type
header for exact match.
content_type_isnt
$t = $t ->content_type_isnt( 'text/html' );
$t = $t ->content_type_isnt( 'text/html' , 'different content type' );
|
Opposite of "content_type_is".
content_type_like
$t = $t ->content_type_like( qr/text/ );
$t = $t ->content_type_like( qr/text/ , 'right content type' );
|
Check response Content-Type
header for similar match.
content_type_unlike
$t = $t ->content_type_unlike( qr/text/ );
$t = $t ->content_type_unlike( qr/text/ , 'different content type' );
|
Opposite of "content_type_like".
content_unlike
$t = $t ->content_unlike( qr/working!/ );
$t = $t ->content_unlike( qr/working!/ , 'different content' );
|
Opposite of "content_like".
delete_ok
$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' });
|
Perform a DELETE
request and check for transport errors, takes the same arguments as "delete" in Mojo::UserAgent, except for the callback.
element_count_is
$t = $t ->element_count_is( 'div.foo[x=y]' , 5);
$t = $t ->element_count_is( 'html body div' , 30, 'thirty elements' );
|
Checks the number of HTML/XML elements matched by the CSS selector with "find" in Mojo::DOM.
element_exists
$t = $t ->element_exists( 'div.foo[x=y]' );
$t = $t ->element_exists( 'html head title' , 'has a title' );
|
Checks for existence of the CSS selectors first matching HTML/XML element with "at" in Mojo::DOM.
$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]' );
|
element_exists_not
$t = $t ->element_exists_not( 'div.foo[x=y]' );
$t = $t ->element_exists_not( 'html head title' , 'has no title' );
|
Opposite of "element_exists".
finish_ok
$t = $t ->finish_ok;
$t = $t ->finish_ok(1000);
$t = $t ->finish_ok( 1003 => 'Cannot accept data!' );
|
Close WebSocket connection gracefully.
finished_ok
$t = $t ->finished_ok(1000);
|
Wait for WebSocket connection to be closed gracefully and check status.
get_ok
$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' });
|
Perform a GET
request and check for transport errors, takes the same arguments as "get" in Mojo::UserAgent, except for the callback.
$t ->get_ok( '//sri:secr3t@/secrets.json' )
->status_is(200)
->json_is( '/1/content' , 'Mojo rocks!' );
$t ->get_ok( '/foo' )->status_is(200);
is $t ->tx->res->dom->at( 'input' )->val, 'whatever' , 'right value' ;
|
head_ok
$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' });
|
Perform a HEAD
request and check for transport errors, takes the same arguments as "head" in Mojo::UserAgent, except for the callback.
$t = $t ->header_exists( 'ETag' );
$t = $t ->header_exists( 'ETag' , 'header exists' );
|
Check if response header exists.
$t = $t ->header_exists_not( 'ETag' );
$t = $t ->header_exists_not( 'ETag' , 'header is missing' );
|
Opposite of "header_exists".
$t = $t ->header_is( ETag => '"abc321"' );
$t = $t ->header_is( ETag => '"abc321"' , 'right header' );
|
Check response header for exact match.
$t = $t ->header_isnt( Etag => '"abc321"' );
$t = $t ->header_isnt( ETag => '"abc321"' , 'different header' );
|
Opposite of "header_is".
$t = $t ->header_like( ETag => qr/abc/ );
$t = $t ->header_like( ETag => qr/abc/ , 'right header' );
|
Check response header for similar match.
$t = $t ->header_unlike( ETag => qr/abc/ );
$t = $t ->header_unlike( ETag => qr/abc/ , 'different header' );
|
Opposite of "header_like".
json_has
$t = $t ->json_has( '/foo' );
$t = $t ->json_has( '/minibar' , 'has a minibar' );
|
Check if JSON response contains a value that can be identified using the given JSON Pointer with Mojo::JSON::Pointer.
json_hasnt
$t = $t ->json_hasnt( '/foo' );
$t = $t ->json_hasnt( '/minibar' , 'no minibar' );
|
Opposite of "json_has".
json_is
$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' );
|
Check the value extracted from JSON response using the given JSON Pointer with Mojo::JSON::Pointer, which defaults to the root value if it is omitted.
$t ->json_is( '' => { foo => [1, 2, 3]}, 'right object' );
|
json_like
$t = $t ->json_like( '/foo/1' => qr/^\d+$/ );
$t = $t ->json_like( '/foo/1' => qr/^\d+$/ , 'right value' );
|
Check the value extracted from JSON response using the given JSON Pointer with Mojo::JSON::Pointer for similar match.
json_message_has
$t = $t ->json_message_has( '/foo' );
$t = $t ->json_message_has( '/minibar' , 'has a minibar' );
|
Check if JSON WebSocket message contains a value that can be identified using the given JSON Pointer with Mojo::JSON::Pointer.
json_message_hasnt
$t = $t ->json_message_hasnt( '/foo' );
$t = $t ->json_message_hasnt( '/minibar' , 'no minibar' );
|
Opposite of "json_message_has".
json_message_is
$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' );
|
Check the value extracted from JSON WebSocket message using the given JSON Pointer with Mojo::JSON::Pointer, which defaults to the root value if it is omitted.
json_message_like
$t = $t ->json_message_like( '/foo/1' => qr/^\d+$/ );
$t = $t ->json_message_like( '/foo/1' => qr/^\d+$/ , 'right value' );
|
Check the value extracted from JSON WebSocket message using the given JSON Pointer with Mojo::JSON::Pointer for similar match.
json_message_unlike
$t = $t ->json_message_unlike( '/foo/1' => qr/^\d+$/ );
$t = $t ->json_message_unlike( '/foo/1' => qr/^\d+$/ , 'different value' );
|
Opposite of "json_message_like".
json_unlike
$t = $t ->json_unlike( '/foo/1' => qr/^\d+$/ );
$t = $t ->json_unlike( '/foo/1' => qr/^\d+$/ , 'different value' );
|
Opposite of "json_like".
message_is
$t = $t ->message_is({ binary => $bytes });
$t = $t ->message_is({ text => $bytes });
$t = $t ->message_is( 'working!' );
$t = $t ->message_is( 'working!' , 'right message' );
|
Check WebSocket message for exact match.
message_isnt
$t = $t ->message_isnt({ binary => $bytes });
$t = $t ->message_isnt({ text => $bytes });
$t = $t ->message_isnt( 'working!' );
$t = $t ->message_isnt( 'working!' , 'different message' );
|
Opposite of "message_is".
message_like
$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' );
|
Check WebSocket message for similar match.
message_ok
$t = $t ->message_ok;
$t = $t ->message_ok( 'got a message' );
|
Wait for next WebSocket message to arrive.
$t ->websocket_ok( '/time' )
->message_ok
->message_like( qr/\d+/ )
->message_unlike( qr/\w+/ )
->finish_ok;
|
message_unlike
$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' );
|
Opposite of "message_like".
new
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' });
|
Construct a new Test::Mojo object. In addition to a class name or Mojo::File 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 config_override
will be set in "config" in Mojolicious as well, which is used to disable configuration plugins like Mojolicious::Plugin::Config, Mojolicious::Plugin::JSONConfig and Mojolicious::Plugin::NotYAMLConfig for tests.
my $t = Test::Mojo->new(curfile->dirname->sibling( 'myapp.pl' ));
|
options_ok
$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' });
|
Perform a OPTIONS
request and check for transport errors, takes the same arguments as "options" in Mojo::UserAgent, except for the callback.
or
Execute callback if the value of "success" is false.
$t ->get_ok( '/bad' )->or( sub { diag 'Must have been Glen!' })
->status_is(200)->or( sub { diag $t ->tx->res->dom->at( 'title' )->text });
|
patch_ok
$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' });
|
Perform a PATCH
request and check for transport errors, takes the same arguments as "patch" in Mojo::UserAgent, except for the callback.
post_ok
$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' });
|
Perform a POST
request and check for transport errors, takes the same arguments as "post" in Mojo::UserAgent, except for the callback.
my $upload = { foo => { content => 'bar' , filename => 'baz.txt' }};
$t ->post_ok( '/upload' => form => $upload )->status_is(200);
$t ->post_ok( '/hello.json' => json => { hello => 'world' })
->status_is(200)
->json_is({ bye => 'world' });
|
put_ok
$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' });
|
Perform a PUT
request and check for transport errors, takes the same arguments as "put" in Mojo::UserAgent, except for the callback.
request_ok
$t = $t ->request_ok(Mojo::Transaction::HTTP->new);
|
Perform request and check for transport errors.
my $tx = $t ->ua->build_tx( FOO => '/test.json' => json => { foo => 1});
$t ->request_ok( $tx )->status_is(200)->json_is({ success => 1});
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' );
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;
|
reset_session
Reset user agent session.
send_ok
$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' );
|
Send message or frame via WebSocket.
$t ->websocket_ok( '/echo.json' )
->send_ok({ json => { test => 'I ♥ Mojolicious!' }})
->message_ok
->json_message_is( '/test' => 'I ♥ Mojolicious!' )
->finish_ok;
|
status_is
$t = $t ->status_is(200);
$t = $t ->status_is(200, 'right status' );
|
Check response status for exact match.
status_isnt
$t = $t ->status_isnt(200);
$t = $t ->status_isnt(200, 'different status' );
|
Opposite of "status_is".
test
$t = $t ->test( 'is' , 'first value' , 'second value' , 'right value' );
|
Call Test::More functions through "handler", used to implement Test::Mojo roles. The result will be stored in "success".
text_is
$t = $t ->text_is( 'div.foo[x=y]' => 'Hello!' );
$t = $t ->text_is( 'html head title' => 'Hello!' , 'right title' );
|
Checks text content of the CSS selectors first matching HTML/XML element for exact match with "at" in Mojo::DOM.
text_isnt
$t = $t ->text_isnt( 'div.foo[x=y]' => 'Hello!' );
$t = $t ->text_isnt( 'html head title' => 'Hello!' , 'different title' );
|
Opposite of "text_is".
text_like
$t = $t ->text_like( 'div.foo[x=y]' => qr/Hello/ );
$t = $t ->text_like( 'html head title' => qr/Hello/ , 'right title' );
|
Checks text content of the CSS selectors first matching HTML/XML element for similar match with "at" in Mojo::DOM.
text_unlike
$t = $t ->text_unlike( 'div.foo[x=y]' => qr/Hello/ );
$t = $t ->text_unlike( 'html head title' => qr/Hello/ , 'different title' );
|
Opposite of "text_like".
websocket_ok
$t = $t ->websocket_ok( '/echo' );
$t = $t ->websocket_ok( '/echo' => { DNT => 1} => [ 'v1.proto' ]);
|
Open a WebSocket connection with transparent handshake, takes the same arguments as "websocket" in Mojo::UserAgent, except for the callback.
$t ->websocket_ok( '/' => { 'Sec-WebSocket-Extensions' => 'permessage-deflate' })
->send_ok( 'y' x 50000)
->message_ok
->message_is( 'z' x 50000)
->finish_ok;
|
SEE ALSO
Mojolicious, Mojolicious::Guides, https://mojolicious.org.