Acme-CPANModulesBundle-Import-PerlDancerAdvent-2018

 view release on metacpan or  search on metacpan

META.json  view on Meta::CPAN

            "class" : "Dist::Zilla::Plugin::CopyrightYearFromGit",
            "name" : "@Author::PERLANCAR/CopyrightYearFromGit",
            "version" : "0.003"
         },
         {
            "class" : "Dist::Zilla::Plugin::IfBuilt",
            "name" : "@Author::PERLANCAR/IfBuilt",
            "version" : "0.03"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaJSON",
            "name" : "@Author::PERLANCAR/MetaJSON",
            "version" : "6.010"
         },
         {
            "class" : "Dist::Zilla::Plugin::MetaConfig",
            "name" : "@Author::PERLANCAR/MetaConfig",
            "version" : "6.010"
         },
         {
            "class" : "Dist::Zilla::Plugin::Authority",
            "name" : "@Author::PERLANCAR/Authority",

META.json  view on Meta::CPAN

      ],
      "zilla" : {
         "class" : "Dist::Zilla::Dist::Builder",
         "config" : {
            "is_trial" : 0
         },
         "version" : "6.010"
      }
   },
   "x_authority" : "cpan:PERLANCAR",
   "x_serialization_backend" : "Cpanel::JSON::XS version 3.0239",
   "x_static_install" : 1
}

META.yml  view on Meta::CPAN

      version: '0.011'
    -
      class: Dist::Zilla::Plugin::CopyrightYearFromGit
      name: '@Author::PERLANCAR/CopyrightYearFromGit'
      version: '0.003'
    -
      class: Dist::Zilla::Plugin::IfBuilt
      name: '@Author::PERLANCAR/IfBuilt'
      version: '0.03'
    -
      class: Dist::Zilla::Plugin::MetaJSON
      name: '@Author::PERLANCAR/MetaJSON'
      version: '6.010'
    -
      class: Dist::Zilla::Plugin::MetaConfig
      name: '@Author::PERLANCAR/MetaConfig'
      version: '6.010'
    -
      class: Dist::Zilla::Plugin::Authority
      name: '@Author::PERLANCAR/Authority'
      version: '1.009'
    -

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

<p>Like any <a href="https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles/">Mojolicious Role</a>, we can use <code>with_roles</code> to create a (mostly anonymous) subclass with the role applied.
You can use the shortcut <code>+</code> to stand in for <code>Test::Mojo::Role::</code>.</p>
<pre class="prettyprint">use Test::Mojo;
my $class = Test::Mojo-&gt;with_roles('+PSGI');</pre>

<p>Then you instantiate that role with the path to the PSGI application, or else the PSGI application itself.</p>
<p>Since you're using roles, which are all about composition, you can also apply other roles that you might <a href="https://metacpan.org/search?q=%22Test%3A%3AMojo%3A%3ARole%22">find on CPAN</a>.</p>
<h2><a name="an_example"></a>An Example</h2>

<p>As an example, let's say we have a simple application script (named <code>app.psgi</code>) that can render a <code>"hello world"</code> or <code>"hello $user"</code> in several formats.
I'll allow a plain text response, JSON, and templated HTML (using the <a href="https://metacpan.org/pod/Dancer2::Template::Simple">simple</a> template to keep this concise).</p>
<pre class="prettyprint">use Dancer2;

set template =&gt; 'simple';
set views =&gt; '.';

any '/text' =&gt; sub {
  my $name = param('name') // 'world';
  send_as plain =&gt; "hello $name";
};

any '/data' =&gt; sub {
  my $name = param('name') // 'world';
  send_as JSON =&gt; { hello =&gt; $name };
};

any '/html' =&gt; sub {
  my $name = param('name') // 'world';
  template 'hello' =&gt; { name =&gt; $name };
};

start;</pre>

<p>And the template (<code>hello.tt</code>) is</p>
<pre class="prettyprint">&lt;dl id="data"&gt;
  &lt;dt id="hello"&gt;hello&lt;/dt&gt;
  &lt;dd&gt;&lt;% name %&gt;&lt;/dd&gt;
&lt;/dl&gt;</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;

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

  -&gt;content_type_like(qr[application/json])
  -&gt;json_is('/hello' =&gt; 'world');

$t-&gt;post_ok('/data' =&gt; form =&gt; { name =&gt; 'rudolph' })
  -&gt;status_is(200)
  -&gt;content_type_like(qr[application/json])
  -&gt;json_is('/hello' =&gt; '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>-&gt;json_is({hello =&gt; '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-&gt;get_ok('/html')
  -&gt;status_is(200)
  -&gt;content_type_like(qr[text/html])
  -&gt;text_is('dl#data dt#hello + dd', 'world');

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


$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>



( run in 0.832 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )