[Catalyst] Testing controller which require login.

luke saunders luke.saunders at gmail.com
Wed May 13 09:20:20 GMT 2009

On Wed, May 13, 2009 at 6:07 AM, Louis Erickson <lerickson at rdwarf.net> wrote:
> I've been neglecting my test scripts, and I'm finally getting to that
> before my app gets any bigger.  I want to make sure I don't break anything
> that's working, and that it works as well as I think it does.
> I'm probably going to have several questions in the next few days about
> tests.  I think I've got a lot of the basics working; tests pass, I have a
> sensible test database and environment.  I can see fixtures from here, I
> think.
> 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.
> I can set a mock user, but then that user is always logged in.  Can that
> mock user be changed by the test scripts, so I can have them log out, log
> in as an administrator, log in as an unprivileged user, etc, and test the
> pages perform correctly?  If so, I've missed that - it would work, I
> think.
> The SSO is available to me as a library.  I'm considering adding a login
> page to the application, so the test scripts can log in to the SSO through
> the application and solve the problem.  Is duplicating a little code (not
> much, most of it is in a module) to make tests work a good idea?
> I'd want the application-specific login page to only be available while
> the test scripts are running.  In production it shouldn't exist or at the
> worst redirect back to the real SSO login page.  Writing a function which
> behaves differently in test seems like a source of errors to me.  Does
> this sound possible?
> Can I just generate the cookie and put it in the cookie store directly
> before the query is made to the application?  I can generate the cookie
> with the SSO library.  I don't see a way to do that through either test
> tool.
> I'm trying to learn and do this in a maintainable, sensible way.  Any help
> you can give is highly appreciated.

I'm not sure I really understand what's quite so complicated about the
SSO. It sounds like it validates some credentials and then sets some
site cookies if successful, which is pretty standard and should work
with the following testing method. If not then please eleborate.

You need a set of fixtures (test dataset) and a test database to
deploy them to. At the start of every test you should populate your
test database with these fixtures.

Test::WWW::Mechanize::Catalyst can handle cookies just fine and
posting to forms is easy enough, which means you can easily use it to
test login.

Here's an example .t file (assumes DBIx::Class, but the principle is
the same for anything):

use strict;
use warnings;

use lib 't/lib';

use MyAppTest;
use Test::More tests => 1;
use Test::WWW::Mechanize::Catalyst 'MyApp';
use HTTP::Request::Common;

my $base = 'http://localhost';
my $mech = Test::WWW::Mechanize::Catalyst->new;

# populate test database with fixtures and get a DBIC schema in return
my $schema = MyAppTest->init_schema({ set => 'sample' });

  my $req = POST( "$base/login", {
    username => 'testuser',
    password => 'testpassword',
  $mech->request($req, $content_type);
  cmp_ok( $mech->status, '==', 200, 'Login successful' );

# if all goes well at this point $mech will be logged in.


package MyAppTest;

use strict;
use warnings;

  # so that the app uses the test config and therefore test database

use MyApp::Util::Fixtures;

sub init_schema {
  my ($class, $args) = @_;

  my $myapp_fixtures = MyApp::Util::Fixtures->new;
  $myapp_fixtures->populate({ schema => 'TestDB', set => ($args->{set}
|| 'default') });
  my $schema = $myapp_fixtures->schema;
  return $schema;

I put the gritty business of populating the database from fixtures in
MyApp::Util::Fixtures. I use DBIx::Class::Fixtures for all this and
have some sample code if you need it.

More information about the Catalyst mailing list