Acme-CPANModulesBundle-Import-PerlDancerAdvent-2018

 view release on metacpan or  search on metacpan

devdata/http_advent.perldancer.org_2018_20  view on Meta::CPAN

<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-&gt;get_ok('/html')
  -&gt;status_is(200)
  -&gt;content_type_like(qr[text/html])
  -&gt;text_is('dl#data dt#hello + dd', 'world');

$t-&gt;post_ok('/html' =&gt; form =&gt; { name =&gt; 'grinch' })
  -&gt;status_is(200)
  -&gt;content_type_like(qr[text/html])
  -&gt;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;
my $twiggy = Twiggy::Server-&gt;new(
  host =&gt; '127.0.0.1',
  server_ready =&gt; sub {
    my $args = shift;
    $url = "ws://$args-&gt;{host}:$args-&gt;{port}/ws";
  },
);
$twiggy-&gt;register_service($app);</pre>

<p>This starts Twiggy bound to localhost on a random port and starts the application using it.
When the server starts, the actual host and port are passed to the <code>server_ready</code> callback which we use to build the test url.
Now you just create a Test::Mojo instance as normal but this time open a websocket to the fully-qualified url that we built above.</p>
<pre class="prettyprint">my $t = Test::Mojo-&gt;new;

$t-&gt;websocket_ok($url)
  -&gt;send_ok({json =&gt; {hello =&gt; 'Dancer'}})
  -&gt;message_ok
  -&gt;json_message_is({hello =&gt; 'browser!'})
  -&gt;finish_ok;

done_testing;</pre>

<p>Unlike the previous examples, this time the connection stays open (but blocked) between method calls.
Per the protocol of the example, we first send a greeting to the Dancer app as a JSON document.
Since so much real-world websocket usage is just serialized JSON messages, Mojolicious provides many JSON-over-WebSocket conveniences.
One such convenience is a virtual websocket frame type that takes a data structure and serializes it as JSON before actually sending it as a text frame.</p>
<p>We then wait to get a message in response with <code>message_ok</code>.
In this case, we expect the application to greet us by calling us "browser!".
Oh well, it doesn't know any better!
We can the test that JSON reply with <code>json_message_is</code> (like <code>json_is</code> above but for websocket messages).
Finally we close the connection, testing that it closes correctly.</p>
<p>Testing WebSockets, even from a Dancer application, is easy!</p>
<h2><a name="conclusion"></a>Conclusion</h2>

<p>Although there are some great testing options in the PSGI space, Test::Mojo has lots of benefits for Dancer and PSGI users.
By using Test::Mojo::Role::PSGI or by running against a locally-bound server, Test::Mojo can be a tool in the toolbox of any PSGI developer.</p>
<h2><a name="author"></a>AUTHOR</h2>

<p>Joel Berger (<a href="https://twitter.com/joelaberger">@joelaberger</a>) has Ph.D. in Physics from the University of Illinois at Chicago. 
He is an avid Perl user, <a href="https://metacpan.org/author/JBERGER">author</a>, and is a member of the Mojolicious Core Team.</p>
<h2><a name="copyright"></a>COPYRIGHT</h2>

<p>Copyright (c) 2018 Joel Berger</p>
</div>

 <div id="disqus_thread"></div>
    <script type="text/javascript">
        /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
        var disqus_shortname = 'danceradvent'; // required: replace example with your forum shortname

        /* * * DON'T EDIT BELOW THIS LINE * * */
        (function() {
            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
            dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
            (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
        })();
    </script>
    <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>




</div>



<div id="footer">
Powered by the  
<a href="http://perldancer.org/" title="Perl Dancer - Perl web framework">
Dancer Perl web framework</a>
</div>
</div>


<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-25174467-2']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';



( run in 0.989 second using v1.01-cache-2.11-cpan-39bf76dae61 )