Welcome to End Point’s blog

Ongoing observations by End Point people

Challenges in testing Dancer 2.0 apps

I've been dabbling in Dancer, and I managed to put together a moderately complex re-hosting of a web application with Dancer 1.0, including a preliminary package of unit tests via Test::More.

Spoiler alert: I don't yet have a solution, but I thought maybe this blog post would organize my thoughts to where someone else might peek in and spot my problem.

A bit of introduction follows for those who may not have been down this path before.

Testing a web application can take one of two general approaches:

  1. Test the underlying code by calling it directly, one program to another, with no web server involved. This is pretty straightforward, although you may have to rig up some replacements for the environment (such as if your code expects CGI parameters, or reacts to things in the web server environment such as cookies, remote IP address, etc.). In any case, you have to recognize that you are now testing the code logic, not the actual interaction of your code as a subsystem in the web server.
  2. Test the web application in its "native environment", by issuing requests to its associated web server and examining the responses (as web pages, JSON, what-have-you). This is much preferred, as it will catch all sorts of subtle bugs, if you haven't exactly reproduced the environment (for instance, "correctly" spelling HTTP_REFERRER in your rigged-up environment, whereas the misspelled HTTP_REFERER is actually in use).

The module Dancer2::Test provides a small set of routines for supporting this second approach. For instance, you can verify that your routes are set up correctly via "route_exists":

route_exists [ GET => '/index.html' ], "We can get the home page";

This behaves like the various test-helper routines in Test::More. If the test succeeds (in this case, a route is defined that matches the path), great; if not, an error message is reported using the string parameter.

You can also verify that the correct page has been delivered without errors:

my $response = dancer_response GET => '/something';
is $response->{status}, 200, '/something returns OK';
like $response->{content}, qr{Something from the /something page};

dancer_response is the preferred way to go about this, because it captures status, headers, and content in one operation, saving you time but also avoiding the potential for disturbing the state of your application by subsequent requests.

Now, this article's title starts with "Challenge", so I should let the other shoe drop. The Dancer2::Test setup doesn't seem to have a way to preserve "state" on the server between requests. So if your application supports, for instance, a "login" page and a "modify my account" page, you don't really have a way to test the latter page, as you can't remain logged in.


Rob Kinyon said...

Consider that your test code is acting as a consumer for the application code under test. Another name for the consumer of a web application is "a browser." The contract between the web application and its consumers entails that the consumer will know how to persist certain things (such as cookies) across requests, forming a session.

So, the immediate solution to your problem is a cookie jar. The standard one in Perl is HTTP::Cookies.

Jeff Boes said...

I figured it was something like that, but I haven't identified the mechanism for associating a cookie jar with the Dancer request. For instance:

my $browser = LWP::UserAgent->new();

but "dancer_request" doesn't seem to have a provision for passing in a user agent. I've been digging through its source, asking on the Perl IRC channel #dancer, etc. without success (yet).