Showing posts with label Test::TCP. Show all posts
Showing posts with label Test::TCP. Show all posts

Friday, 28 June 2013

LWP::UserAgent: a browser

Last time we had a lot of fun playing with Test::TCP and LWP::UserAgent, but we're not done yet with these toys, there's something more I want to say about them.

So we can test our Dancer2 route:

my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://127.0.0.1:$port/route/");

ok($res->is_success);
is($res->content, 'my content');

Sometime this is not enough. In the code I'm developing redirect is important as success, how can I test this? Well, I suppose...


my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://127.0.0.1:$port/redirecting_route/");

is($res->code, 302);

But result is:

# got: '200'
# expected: '302'

What's wrong?

You could think (I did, at last) that LWP::UserAgent is just a "tester", something to do "ping" on internet sites and see what come back. It's not, LWP::UserAgent is powerful because it's a complete working browser, like Firefox, Opera, Chrome or IE. It's not good in rendering pages, ok, but the data-flow it can manage is the same you can see while surfing the web.

What a normal browser like Firefox do when there's a redirect?

Simple:

CALL -> 302 -> NEW PAGE -> 200

Though you're giving back a redirect for a route you still want to give a page, a success result, at the end of the navigation chain. LWP::UserAgent follow all the chain without asking for nothing, as your browser, bringing back the final 200 response.

Ok, LWP::UserAgent is cool and I'll use it to fetch good pron, I promise, but now I'm looking for something that check my Dancer2 App doing the right redirect!

Test is just a bit more complex:

my $ua = LWP::UserAgent->new;
my $res = $ua->get("http://127.0.0.1:$port/redirecting_route/");

is($res->is_success and $res->previous);

Call ends with a 200 but a previous URL was registered and we're redirecting from there. You can see this URL in $res->previous but knowing it's there is enough to say we're redirecting.

Something more about the browser powers of LWP::UserAgent?
What if you need to test sessions?  How Dancer2 trace client sessions? With a session cookie, obviously. And LWP::UserAgent can manage them too? Obviously again, but remember to give it  a cookie_jar to use:

$ua->cookie_jar({file => "cookies.txt"});

Rememeber. With LWP::UserAgent you're not just trying out your site. You're literally surfing it!

Wednesday, 26 June 2013

Dancer2::Test. undiscovered countries

If you want to do serious developement, talk with serious people and produce serious packages you have to use testing scripts A LOT. Dancer2 comes with its Dancer2::Test library, a lot of good features... and few gloomy mysteries.

Let me start with a little tip: whenever you start testing import Dancer2::Test and specify which apps you're going to use:

use Dancer2::Test apps => ['app1', 'app2'];

Probably you already knew it, but I had to dig up a little in Dancer2 docs to find that this is a "must have" for Dancer2::Test and everytime I spend time to find something I think that what I find deserves few words on this blog. When you'll search for it like me, I'll be there, on the first page of Google, waiting for you.

But I was talking about gloomy mysteries so now we'll talk about strage behaviours of Dancer2::Test, triggered by nasty (but legit) coding choices.

In my first article I talked about before hooks and forward.

See this example:

hook before => sub {
      my $context = shift;
      if (request->path_info eq '/ghost_path' )
      {
           $context->response( forward('/solid_path' ));
           $context->response->halt;
      }
}

You obviously defined a dispatcher for '/solid_path' but there's no reason you have to define a dispatcher for '/ghost_path'. Still, calling '/ghost_path' will return a page (HTTP code 200! Win!).

Now we'll write a test for this:

response_status_is ['GET' => '/ghost_path'], 200;

Result is:

#          got: '404'
#     expected: '200'


What? Aaaargh, Wotan worshipper rises is axe!

Dancer2::Test does a bit of introspection in your Dancer2 App. When you ask for a path it simply tries to find it in the defined dispatchers, where there isn't. It can't deduce that all will go well thanks to the before hook so the test fails.

But. Wotan. Worshipper. Wants. To. Test!

Well, servers never lie. Never. If the test could be done with a real server and not just... playing with pms...

Here comes Test::TCP.


use strict;
use warnings;
use Test::More;

use Test::TCP;
use LWP::UserAgent;


Test::TCP::test_tcp(
    client => sub {
        my $port = shift;
        my $ua = LWP::UserAgent->new;
        my $res = $ua->get("http://127.0.0.1:$port/ghost_path/");
        ok($res->is_success);
    },
    server => sub {
        my $port = shift;
        use Dancer2;
        use multilang;
        set(show_errors  => 1,
            startup_info => 0,
            environment  => 'developement',
            port         => $port
            );

        Dancer2->runner->server->port($port);
        start;
    },
);
done_testing;


This is a real server launched by a script and reached by a real client. It's just as the actual site. And the test goes well...

What we learned today? Running a server and click in a browser is the only reliable way to test something. Let a machine doing it by itself without all the boring part (open a browser... digit the url... go on Facebook while loading...) is good.
Obviously, in many many cases Dancer2::Test, plain and simple, is enough and it's the right way to do things, because it's a bit less complex working with it. But keep an eye on exceptions...