Created
October 2, 2010 13:15
-
-
Save marcusramberg/607631 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=encoding utf8 | |
=head1 NAME | |
Mojolicious::Guides::Testing - TDD gives you Mojo. | |
=head1 OVERVIEW | |
Testing your L<Mojolicious> application. | |
=head1 CONCEPTS | |
unit tests, client side code, integration tests | |
=head1 The Basics | |
=head2 Testing your Mojolicious Application | |
Rather than being just another buzz word, Test Driven Development is a radical | |
change in the way we develop software. In order to allow easy testing of your | |
Mojolicious application, we include Test::Mojo, a way to perform integration | |
tests directly against your application. This works well with the | |
standard Test::More suite used in most perl distributions: | |
use Test::More tests=>3; | |
use Mojo::Test; | |
my $t=Mojo::Test->new(app => 'MyApp'); | |
$t->get_ok('/') | |
->status_is(200) | |
->content_like(qr/Hello/); | |
Note that the test methods are chained for your convenience. If you need to | |
access the request and response objects for further testing,you can get to | |
them through $t->tx (short for transaction). L<Test::Mojo> also supports a lot | |
of other ways to test your request. See the POD for L<Test::Mojo> for all | |
the methods you can use to test your application. | |
=head2 Testing your Mojolicious::Lite Application. | |
Since a Mojolicious::Lite application is a script rather than a package, we | |
need to do a little extra work to load it in our t/ files: | |
use FindBin; | |
$ENV{MOJO_HOME} = "$FindBin::Bin/../"; | |
require "$ENV{MOJO_HOME}/myapp.pl"; | |
my $t = Test::Mojo->new; | |
=head1 Testing the Parts | |
=head2 Using Mojo::DOM for client-testing | |
Recent versions of Mojo include Mojo::DOM, a simple CSS-selector based | |
liberal XML parser. In practice, it can even parse most HTML-documents, | |
making it ideal for testing the interface of your application. | |
Mojo::DOM using the same CSS selectors jQuery use to target your | |
javascript actions. The implementation is so similar we recommend you | |
refer to their documentation at api.jquery.org/category/selectors/ for | |
a better understanding of the syntax. | |
Of course, being a server side implementation, there are some things that | |
weren't useful to implement in Mojo::DOM, notably the :animated selector and | |
the :enabled selector. There are also some convenience selectors in jquery | |
that hasn't been implemented yet in L<Mojo::DOM>. | |
Still, the implementation is quite full featured, including pseudo | |
operators like ':checked' and ':empty', which can be quite useful when | |
testing your forms. You can even do things like ':checked[value="foo"]'. | |
Mojo also includes support for selector groups and ':first-child' and | |
even ':nth-child'. | |
Mojo::Test has some testing methods which let you leverage Mojo::DOM | |
in your unit tests, including $t->element_exists, $t->text_is and | |
$t->text_like | |
Here's a simple example: | |
$t->get_ok('/') | |
->element_exists('.header h3','Check existence of header title') | |
->text_is('.foo:checked'),'groovy','Check text for checked element') | |
Of course, if you want to write proper unit tests for the user interface | |
you could use L<MojoX::Renderer> directly to render templates with mock | |
values, and parse them through Mojo::DOM to test the content. To set this | |
up, you can create a blank Mojo controller and set mock stash values: | |
my $c = MojoX::Dispatcher::Routes::Controller->new(app => Mojo->new); | |
$c->stash->{template} = 'testing'; | |
... | |
$c->stash->{value1} = 'foo'; | |
is_deeply [$r->render($c)], ['Hello Mojo!', 'text/plain'], 'desc'; | |
However, doing it this way stops you from using Test::Mojo's convenience | |
methods. | |
=head2 Testing your Web Service | |
If your web service is XML based, you can use the same methods as in the | |
previous section to test your XML structures with ease. Mojo::DOM is not | |
just for HTML, you can use it for valid XML structure. | |
If you prefer JSON (like most of us), L<Test::Mojo> provides a json_content_is | |
method to test your generated data structures. This works about the same | |
as is_deeply: | |
$t->json_content_is(['foo','bar',{baz => 'quz'}]); | |
Also, if you are writing a RESTish web service, remember to test your | |
status codes and different verbs as well. | |
=head2 Testing your Model | |
Of course, in every well structured application, most of your logic should | |
residen in the model layer. Mojolicious is model-agnostic to the point of | |
not even including Model-adapters like for instance Catalyst. This means | |
that you can test your data models completely separate from the rest of | |
your Mojolicious application. Just write unit tests as you would normally | |
do. | |
=head2 Going low-level. | |
Some times you need to test things at the wire level. Taking a page out | |
of the Mojo playbook, here's how Sebastian does this in the Mojo test | |
suite itself. First, these tests require us to set up a socket, so we | |
need to make sure we have a working socket implementation. | |
plan skip_all => 'working sockets required for this test!' | |
unless Mojo::IOLoop->new->generate_port; | |
It's convenient to set up a Mojolicious::Lite app to use for testing. | |
Just create it as normal, then instead of starting it, we create a | |
simple server. | |
my $client = Mojo::Client->singleton->app(app); | |
my $port = $client->ioloop->generate_port; | |
my $id = $client->ioloop->listen( | |
port => $port, | |
on_accept => sub { ... }, | |
on_read => sub { ... }, | |
on_error => sub { ... }, | |
); | |
In the callback methods, you can implement custom behaviour needed for | |
your tests. Then you just use the client to make requests against the | |
server. | |
$tx = $client->get("http://localhost:$port/mock"); | |
Then use normal Test::More methods to test your $tx->res. | |
=head2 Testing Web Sockets | |
Testing web sockets can be accomplished fairly easily using the built-in | |
L<Mojo::Client>. Load your web socket using app and pass it to a Mojo::Client | |
my $client = Mojo::Client->new->app($app); | |
Since a web socket doesn't really follow a request response model we can't use | |
L<Mojo::Test>. Here's a simple example of how to test an interaction. | |
my $req_number = 0; | |
$client->websocket( | |
# First request | |
'/' => sub { | |
my $self = shift; | |
$self->receive_message( | |
sub { | |
my ($self, $message) = @_; | |
if ($req_number == 0) { # first request | |
like($message, qr/{"text":"Guest\d+ has joined chat"}/); | |
} elsif | |
... | |
# end of last message | |
$self->finish; | |
} | |
$req_number++; | |
}); | |
This technique can also be combined with a set of $self->send_message method | |
calls to simulate a full conversation. notice the $self->finish at the end of | |
the last message to end the conversation. | |
=cut |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment