Acme-CPANModulesBundle-Import-PerlDancerAdvent-2018
view release on metacpan or search on metacpan
devdata/http_advent.perldancer.org_2018_20 view on Meta::CPAN
set views => '.';
any '/text' => sub {
my $name = param('name') // 'world';
send_as plain => "hello $name";
};
any '/data' => sub {
my $name = param('name') // 'world';
send_as JSON => { hello => $name };
};
any '/html' => sub {
my $name = param('name') // 'world';
template 'hello' => { name => $name };
};
start;</pre>
<p>And the template (<code>hello.tt</code>) is</p>
<pre class="prettyprint"><dl id="data">
<dt id="hello">hello</dt>
<dd><% name %></dd>
</dl></pre>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">dl</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt">dt</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd">dd</a> tags...
The HTML I've built, while nice for display isn't necessarily nice for querying programmatically, this is on purpose for the example.</p>
<h2><a name="the_tests"></a>The Tests</h2>
<p>Of course we could start the application with <a href="https://metacpan.org/pod/distribution/Plack/script/plackup">plackup</a> but that's not what we're trying to do.
I'll break the test script down a bit but if you want to see any of these files look at the <a href="https://github.com/MojoliciousDotIO/mojolicious.io/tree/master/blog/2018/12/20/testing-dancer/ex">blog repo</a> for a full listing.
Instead, let's load this into a test script.</p>
<pre class="prettyprint">use Mojo::Base -strict;</pre>
<p>Now if you aren't familiar, <code>use Mojo::Base -strict</code> is a quick way to say</p>
<pre class="prettyprint">use strict;
use warnings;
use utf8;
use IO::Handle;
use feature ':5.10';</pre>
<p>but saves a lot of typing.
Next we load the necessary testing libraries.
Then make an instance of <code>Test::Mojo</code> composed with the <code>PSGI</code> role and make a new instance that points to the app we want to test.</p>
<pre class="prettyprint">use Test::More;
use Test::Mojo;
my $t = Test::Mojo->with_roles('+PSGI')->new('app.psgi');</pre>
<p>With that out of the way, on to the tests!
In our first tests we'll focus on the plain text endpoint <code>/text</code>.</p>
<pre class="prettyprint">$t->get_ok('/text')
->status_is(200)
->content_type_like(qr[text/plain])
->content_is('hello world');</pre>
<p>Each of the above method calls is a test.
The first, <code>get_ok</code>, builds a transaction and requests the resource.
Since the url is relative, it is handled by the app (if we wanted we could request and web resource too using a fully qualified url).
The transaction is stored in the tester object (<code>$t</code>) and all following tests will check it until it is replaced by the next request.</p>
<p>The remaining tests are reasonably self-explanatory, we check that the response status was 200, that we got a content type header that we expected and that its content is as we expect.
The content has already been utf-8 decoded, and the script has implicitly <code>use utf8</code>, so if you expected unicode, you can compare them easily.
The tests return the tester object so chaining is possible, making for visually clean sets of tests.</p>
<p>The next test is similar but this one uses the standard <a href="https://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> style request generation to build a query string naming Santa for our greeting.
The tests are all the same except of course that it checks that the content greets Santa.</p>
<pre class="prettyprint">$t->get_ok('/text', form => { name => 'santa' })
->status_is(200)
->content_type_like(qr[text/plain])
->content_is('hello santa');</pre>
<p>Moving on we request the data endpoint, both without and with a query, then similarly test the responses.</p>
<pre class="prettyprint">$t->get_ok('/data')
->status_is(200)
->content_type_like(qr[application/json])
->json_is('/hello' => 'world');
$t->post_ok('/data' => form => { name => 'rudolph' })
->status_is(200)
->content_type_like(qr[application/json])
->json_is('/hello' => 'rudolph');</pre>
<p>You can see we use the <code>json_is</code> method to test the responses.
Now, the test could have been <code>->json_is({hello => 'rudolph'})</code> if had wanted to test the entire document.
By passing a <a href="https://mojolicious.org/perldoc/Mojo/JSON/Pointer">JSON Pointer</a> I can inspect only the portions I'm interested in.</p>
<p>Finally I'm going to test the HTML endpoint.
As I said above, the result resists easy parsing.
We want to test the <code>dd</code> tag contents that follows a <code>dt</code> tag with the id <code>hello</code>, all inside a <code>dl</code> tag with the id <code>data</code>.
That would be a monstrous regexp (hehe).
However it is a piece of cake using <a href="https://mojolicious.org/perldoc/Mojo/DOM/CSS">CSS Selectors</a>.</p>
<pre class="prettyprint">$t->get_ok('/html')
->status_is(200)
->content_type_like(qr[text/html])
->text_is('dl#data dt#hello + dd', 'world');
$t->post_ok('/html' => form => { name => 'grinch' })
->status_is(200)
->content_type_like(qr[text/html])
->text_is('dl#data dt#hello + dd', 'grinch');
done_testing;</pre>
<p>In this year's Mojolicious advent calendar, we've already seen <a href="https://mojolicious.io/blog/2018/12/05/compound-selectors/">some</a> <a href="https://mojolicious.io/blog/2018/12/14/a-practical-example-of-mojo-dom/">great</a> <a href="https...
The point remains however, testing HTML responses with CSS selectors allows you to make your tests targetd in a way that allows you to write more and better tests since you don't have to hack around extracting the bits you want.</p>
<h2><a name="testing_websockets"></a>Testing WebSockets</h2>
<p>Ok so that's great and all, but of course now it comes to the point you've all been waiting for: can you test WebSockets?
As Jason Crome mentioned in his <a href="http://advent.perldancer.org/2018/13">Twelve Days of Dancer</a> "State of Dancer", you can now dance with WebSockets via <a href="https://metacpan.org/pod/Dancer2::Plugin::WebSocket">Dancer2::Plugin::WebSocket...
<p>Well, so far not via the role I showed above.
It might be possible, but it would involve learning deep PSGI magick that I'm not sure I'm smart enough to do; patches welcome obviously :D.</p>
<p>Still I mentioned above that Test::Mojo can test anything it can access via an fully qualified URL, so let's just start up a server and test it!
I'll use the <a href="https://github.com/yanick/Dancer2-Plugin-WebSocket/tree/releases/example">example bundled with the plugin</a> for simplicty.</p>
<pre class="prettyprint">use Mojo::Base -strict;
use EV;
use Test::More;
use Test::Mojo;
use Twiggy::Server;
use Plack::Util;
my $app = Plack::Util::load_psgi('bin/app.psgi');
my $url;
( run in 0.866 second using v1.01-cache-2.11-cpan-39bf76dae61 )