[Catalyst] Testing controller which require login.
Peter Edwards
peter at dragonstaff.com
Wed May 13 09:32:13 GMT 2009
2009/5/13 Louis Erickson <lerickson at rdwarf.net>
>
> I haven't been able to get a test user logging in and out yet, though.
>
> I have several sections of my application that redirect the user away if
> they are not logged in. I have another that returns a 404 if they don't
> have access, so prying users can't find it (as easily).
>
> My tests are currently based on the stubs generated by myapp_create,
> and use Catalyst::Test. I could move them to WWW::Mechanize::Catalyst if
> it would help.
>
> I can't tell if Catalyst::Test supports cookies. Does it? Or do I need
> to use WWW::Mechanize::Catalyst for that?
>
> I've read some suggestions on how to go about this, and found a couple.
>
> 1> Use WWW::Mechanize::Catalyst to log the user in
> 2> Use the mock user in config to set a user who is logged in.
>
> The problem with really logging the user in is that the login is a set of
> site-wide cookies generated by a Single Sign On system. That system may
> or may not have a test instance running. If it does, cookies stored from
> it probably won't work with cookies from 'localhost' like the local test
> app wants.
>
The approach we used a year or two back was
1. In your application config file MyApp/conf/myapp.pl set up multiple
authentication realms
authentication =3D> {
default_realm =3D> 'members',
realms =3D> {
members =3D> {
credential =3D> {
class =3D> 'Password',
password_field =3D> 'password',
password_type =3D> 'crypted',
},
store =3D> {
...
},
},
debug =3D> {
credential =3D> {
class =3D> 'Password',
password_field =3D> 'password',
password_type =3D> 'clear',
},
store =3D> {
class =3D> 'Minimal',
users =3D> {
test =3D> {
login =3D> 'user',
password =3D> 'pass',
roles =3D> [qw/ user /],
},
admin =3D> {
login =3D> 'admin',
password =3D> 'pass',
roles =3D> [qw/ admin /],
},
},
},
},
2. In root controller that checks authentication, have a case that checks
for environment variables DEBUG_REALM, DEBUG_USER, DEBUG_PASS and if set use
them to perform $c->authenticate() manually rather than redirecting to the
login screen
sub auto : Private {
my ($self, $c) =3D @_;
# Allow unauthenticated users to reach the login page.
if ($c->action eq $c->controller('Web')->action_for('login')) {
return 1;
}
# allow access during interactive debugging or from test scripts
if ( $ENV{DEBUG_USER} ) {
my $realm =3D $ENV{DEBUG_REALM}||'default';
if ( $realm eq 'debug' ) # auth against built in debug users in
configuration
{
$c->authenticate({
username =3D> ($ENV{DEBUG_USER}||'test'), # 'username' needed
for 'debug' realm Minimal authentication
password =3D> ($ENV{DEBUG_PASS}||'pass'),
}, $realm )
|| die "cannot perform debug authentication";
}
else # auth against normal database backend
{
$c->authenticate({
login =3D> ($ENV{DEBUG_USER}||'test'), # 'login' needed for
default 'members' realm
password =3D> ($ENV{DEBUG_PASS}||'pass'),
}, $realm )
|| die "cannot perform env authentication";
}
}
if (!$c->user_exists) {
$c->response->redirect($c->uri_for('/web/login/' .
$c->request->action));
# Return 0 to cancel 'post-auto' processing and prevent use of
application
return 0;
}
# resend cookie to extend its life
$c->session_expires;
return 1; }
# normal interactive login point
sub login :Local {
my ($self, $c, @path) =3D @_;
my $username =3D $c->request->params->{username} || "";
my $password =3D $c->request->params->{password} || "";
if ($username && $password) {
if ($c->authenticate({ login =3D> $username, password =3D> $passwor=
d }) ) {
...
3. Add a couple of helper scripts that run the test web server with
different auth realms for debugging/testing
script/myapp_server_with_auth_envvars.pl
#!/bin/sh
# run test server with auto auth
DEBUG_USER=3Dcust DEBUG_PASS=3Dcust DEBUG_REALM=3Dmembers perl -d
script/myapp_server.pl -k
script/myapp_server_with_debugrealm_auth_envvars.pl
#!/bin/sh
# run test server with debug auth
DEBUG_USER=3Dtest DEBUG_PASS=3Dpass DEBUG_REALM=3Ddebug perl -d
script/myapp_server.pl -k
4. Basic testing using Catalyst::Test
use strict;
use warnings;
use Test::More tests =3D> 2;
BEGIN { use_ok 'Catalyst::Test', 'MyApp' }
ok( request('/web/login')->is_success, 'Request should succeed' );
...
5. Web testing
Set up a test database/fixture with known data, start up web server using
debug auth realm via script/myapp_server_with_auth_envvars.pl and then use
Test::WWW::Mechanize::Catalyst to exercise your application
use strict;
use warnings;
use Test::More;
eval "use Test::WWW::Mechanize::Catalyst 'MyApp'";
plan $@
? ( skip_all =3D> 'Test::WWW::Mechanize::Catalyst required' )
: ( tests =3D> 36 );
ok( my $mech =3D Test::WWW::Mechanize::Catalyst->new, 'Created mech object'=
);
# logout screen redirects to login screen
$mech->get_ok( 'http://localhost/web/logout', 'logout' );
$mech->content_contains('Log on using', 'logout redirects to login screen');
diag "check login authentication is required to access extranet screens";
#
# turn off automatic redirect follow so we can check response code
$mech->requests_redirectable([]);# 1. check redirect code and location
$mech->get( 'http://localhost/web/extranet/list/booking' );
is($mech->response->headers->{status}, 302, 'unauthed user is
redirected away from page requiring auth');
like($mech->response->headers->{location}, qw|/web/login/|, 'redirect
is to login page');
# 2. check all protected paths
for (qw|
index
home
booking
...
|)
{
my $path =3D '/web/extranet/'.$_;
$mech->get( 'http://localhost/'.$path);
is($mech->response->headers->{status}, 302, 'unauth redirect for '.$path);
}
# allow automatic redirect again
$mech->requests_redirectable([qw/ GET HEAD POST /]);
# get login screen
$mech->get_ok( 'http://localhost/web/login', 'get login' );
$mech->content_contains('Log on using', 'login contains Log on using');
# login
$mech->submit_form(
fields =3D> {
username =3D> 'cust',
password =3D> 'cust',
},
);
# check logged in successfully
$mech->content_contains('Welcome to the', 'successfully logged in screen');
diag "screen tests";
$mech->get_ok( 'http://localhost/web/extranet/home', 'get home' );
$mech->content_contains('Welcome to the', 'home contains welcome');
$mech->get_ok( 'http://localhost/web/extranet/list/booking', 'list/booking'=
);
$mech->content_contains('25843011', 'booking list contains booking 25843011=
');
...
I hope that helps and doesn't get too mangled by the mailer.
Regards, Peter
http://perl.dragonstaff.co.uk
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20090513/325c6=
a52/attachment.htm
More information about the Catalyst
mailing list