NAME
Dancer2::Manual::Testing - Writing tests for Dancer2
VERSION
version 2.0.0
Basic application testing
Since Dancer2 produces PSGI applications, you can easily write tests using Plack::Test and provide your Dancer application as the app for testing.
A basic test (which we also scaffold with dancer2) looks like this:
use strict;
use warnings;
use Test::More tests => 4;
use Plack::Test;
use HTTP::Request::Common;
use_ok('MyApp');
# create an application
my $app = MyApp->to_app;
isa_ok( $app, 'CODE' );
# create a testing object
my $test = Plack::Test->create($app);
# now you can call requests on it and get responses
# requests are of HTTP::Request
# responses are of HTTP::Response
# "GET" from HTTP::Request::Common creates an HTTP::Request object
my $response = $test->request( GET '/' );
# same as:
# my $response = $test->request( HTTP::Request->new( GET => '/' ) );
ok( $response->is_success, 'Successful request' );
is( $response->content, 'OK', 'Correct response content' );
Read the documentation for HTTP::Request and HTTP::Request::Common to see the different options for sending parameters.
Subtests
Tests can be separated using Test::More's subtest
functionality, thus creating multiple self-contained tests that don't affect each other.
Assuming we have a different app that we want to test:
# MyApp.pm
package MyApp;
use Dancer2;
set serializer => 'JSON';
my %users = (
jason => {
name => 'Jason',
likes => 'planes',
},
yanick => {
name => 'Yanick',
likes => 'orchids',
}
);
get '/:user' => sub {
my $user = route_parameters->get('user');
my $user_data = $users{$user};
if( $user_data ) {
return { user => $user_data };
}
status 404;
return {
error => 1,
message => 'user not found'
}
};
1;
This is an example of tests for that route ensuring that we have the correct behavior with regard to the user parameter.
# param.t
use strict;
use warnings;
use Test::More;
use Plack::Test;
use HTTP::Request::Common;
use JSON qw/ decode_json /;
use MyApp;
# swap 'null' for 'note' to see the logs in your TAP output
MyApp::set( logger => 'null' );
my $test = Plack::Test->create( MyApp->to_app );
subtest 'An empty request' => sub {
my $res = $test->request( GET '/' );
is $res->code => 404, 'user not provided, so not found';
};
subtest 'Request with invalid user' => sub {
my $res = $test->request( GET '/sawyer_x' );
ok !$res->is_success, 'user not found';
is $res->code => 404, 'ressource not found';
is decode_json($res->content)->{message}, 'user not found', 'error message';
};
subtest 'Request with valid user' => sub {
my $res = $test->request( GET '/jason' );
ok $res->is_success, 'user found';
is decode_json($res->content)->{user}{likes}, 'planes', 'data present';
};
done_testing();
Cookies
To handle cookies, which are mostly used for maintaining sessions, the following modules can be used:
Taking the previous test, assuming it actually creates and uses cookies for sessions (see cookie and perhaps Dancer2::Session::Cookie for more information on how to do that):
# ... all the use statements
use HTTP::Cookies;
my $jar = HTTP::Cookies->new;
my $test = Plack::Test->create( MyApp->to_app );
subtest 'Request with invalid user' => sub {
my $req = GET '/sawyer_x';
$jar->add_cookie_header($req);
my $res = $test->request($req);
ok !$res->is_success, 'Request failed';
$jar->extract_cookies($res);
ok !$jar->as_string, 'All cookies deleted';
};
subtest 'Request with user' => sub {
my $req = GET '/jason';
$jar->add_cookie_header($req);
my $res = $test->request($req);
ok $res->is_success, 'Successful request';
$jar->extract_cookies($res);
ok !$jar->as_string, 'All cookies deleted';
};
done_testing();
If you don't want to use an entire user agent for this test, you can use HTTP::Cookies to store cookies and then retrieve them:
use strict;
use warnings;
use Test::More tests => 3;
use Plack::Test;
use HTTP::Request::Common;
use HTTP::Cookies;
use_ok('MyApp');
my $url = 'http://localhost';
my $jar = HTTP::Cookies->new();
my $test = Plack::Test->create( MyApp->to_app );
subtest 'Create session' => sub {
my $res = $test->request( GET "$url/login" );
ok( $res->is_success, 'Successful login' );
# extract cookies from the response and store in the jar
$jar->extract_cookies($res);
};
subtest 'Check session' => sub {
my $req = GET "$url/logout";
# add cookies to the request
$jar->add_cookie_header($req);
my $res = $test->request($req);
ok( $res->is_success, 'Successful logout' );
like(
$res->content,
'Successfully logged out',
'Got correct log out content',
);
};
Please note that the request URL must include scheme and host for the call to "add_cookie_header" in HTTP::Cookies to work.
Accessing the configuration file
By importing Dancer2 in the test files, there is full access to the configuration using the imported keywords:
use strict;
use warnings;
use Test::More;
use Plack::Test;
use HTTP::Request::Common;
use MyApp;
use Dancer2;
my $appname = config->{'appname'};
diag "Testing $appname";
# ...
Plugins
In order to test plugins, you can create an application on the spot, as part of the test script code, and use the plugin there.
use strict;
use warnings;
use Test::More tests => 2;
use Plack::Test;
use HTTP::Request::Common;
{
package MyTestApp;
use Dancer2;
use Dancer2::Plugin::MyPlugin;
get '/' => sub { my_keyword };
}
my $test = Plack::Test->create( MyTestApp->to_app );
my $res = $test->request( GET '/' );
ok( $res->is_success, 'Successful request' );
is( $res->content, 'MyPlugin-MyKeyword', 'Correct content' );
AUTHOR
Dancer Core Developers
COPYRIGHT AND LICENSE
This software is copyright (c) 2025 by Alexis Sukrieh.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.