[Catalyst] Testing controller which require login.

Tomas Doran bobtfish at bobtfish.net
Fri May 15 11:09:29 GMT 2009

Louis Erickson wrote:
> Just today I spent time writing up some documentation patches you pointed 
> out I could contribute on my blog.  I'm going to try and keep adding 
> things where I can.

Applied in another thread - keep up the good work :)

> Random question about that... if I have diffs to send to the list, are 
> they preferred as part of the body or an attachment to the message so line 
> wrap and such don't mangle them?

Attachment definitely best.

Alternatively, feel free to turn up on irc and get a commit bit so you 
can just commit stuff, that's highly encouraged for docs :)

>> Well volunteered in advance. ;_)
> Where would these best go?  The Catalyst dev wiki?  I actually ask this 
> about several different parts, now that I think of it.

Yeah, I have a feeling that some of it should go in Catalyst::Test, and 
possibly Test::WWW::Mechanize::Catalyst, and some in Catalyst::Manual..

However throwing things onto the wiki as a 'work in progress', or if 
don't think they're generally applicable to the manual is also great :)

>> Technique 1:
>>> I can't tell if Catalyst::Test supports cookies.  Does it?  Or do I need to
>>> use WWW::Mechanize::Catalyst for that?
>> It doesn't support cookies per-se, but it's not hard to handle this yourself.
>> For a nice neat example against the tutorial of testing login, see:
>> http://github.com/bobtfish/catalyst-app-tutorial-kiffin-authissues/blob/cbb7f692676ecd51805dd7cc6cf4393ff6c208c5/t/01app.t
> I see this is checking the request header after the request is run.  To be 
> able to generate a login cookie, I'd have to generate a cookie string, and 
> add it to the request.  The documentation says the request only supports 
> setting the host value, so I couldn't do that.  Am I missing something?

Yes, I think you are.. For more complex cases, I use 
HTTP::Request::Common to get a $req object, which I can then poke with 

So I make my login request, extract the cookie value, then stick that 
back into the Cookie header for each subsequent request.

> It looks like that would be reasonably straightforward, by adding
> code to Catalyst::Test::_customize_request to handle the cookies.
> Would that be a worthwhile change?  I'm not sure.  It might just be better 
> to tell someone who needs that to use a different test method, such as 
> Mechanize.  Myself, I think I'd rather change test harnesses than support 
> the new code.

Yeah, it'd be nice to have some basic cookie handling with 
Catalyst::Test, so that you could ask it to keep and send back cookies 
it was given.

If you'd be prepared to work on that, that would be super!

>> Technique 3:
>>> 2> Use the mock user in config to set a user who is logged in.


> Considering my test environment already uses a separate configuration 
> file, telling the authentication system to use a class like this would be 
> straightforward.  This is probably the least work for me, but only because 
> I already have the tests already using a different configuration.
> Can a test get to Catalyst's configuration at run time, to change what 
> users is mocked in by the authentication module, or would a class it can 
> fiddle with like this be required.  Can I reach TestApp->config() from 
> here?

It can't if you're running the tests remotely, but if you're within the 
same process (the default if you're just using Catalyst::Test), then sure!

I'd probably use package variables as shown before, so you just say: 
$My::Mock::User::id = 'fred'; within your test script...

> I'm not sure how those cookies are serialized and if it's going to be a 
> pain to get the libraries to let me do it from a different place.
> This would be one of my later choices.  It's really a special case of 
> fixtures - it's just data to load before tests.  This is time-sensitive 
> data which must be calculated, so I can't just store one.

Exactly :) And again, if your TestApp is in the same process as the 
tests, then you _can_ just call methods on TestApp-> without a request 
context / with a fake context.

I'm sure it would involve a bit of poking and prodding, but would be 
fairly easy to do..

I'd really like to see this as a tutorial / how-to documentation somewhere..

> It does let you test a bunch of other useful things - redirects, etc - 
> that people might well benefit from seeing how to do.
> Where would the best place for an elaboration of this technique be?  A 
> cookbook entry in Catalyst::Test, so it is easily found?  A page on the 
> Catalyst dev wiki?

I think some short notes in Catalyst::Test itself, and extending the 
'Testing' section in Catalyst::Manual would probably be best.

> 3 does sound more maintainable to me; the user is simply by the test 
> script and you don't have to mess about with faking a login/logout and 
> depending on cookies or whatever.
> I don't know how difficult it would be to implement 5 cleanly for every 
> session store, and for every authentication system.  I'm not sure it's 
> even possible, as not every authentication method uses cookies; the HTTP 
> authenticator just checks the headers doesn't it, and the mocked user 
> would work there, never bothering to check.

Ah yes - 5 is going to be auth/session/store dependent. I was really 
only suggesting that if you use this technique, then you write a how-to 
for your combination :)

> That doesn't mean general access to the stored session from a test script 
> might not be useful, but I'm not sure it's generally useful.  Or am I 
> misunderstanding how Catalyst stores the cookies... I thought they just 
> went in to the session.

The cookie value is basically the key by which the session is 
stored/retrieved, so (given cookies are being used), creating / getting 
/ setting sessions in a test should be fairly easy to arrange in a 
generic way, no matter what your session storage backend.

> I just looked at the existing authentication plugins, hoping one of the 
> current ones would allow this without new code.  I don't see one that can 
> do so by itself.  Catalyst::Authentication::User::Mock, perhaps?  Or am I 
> missing something?

No, I think that you'd need a new class, and that's the class name I was 
thinking of. :)

> Of course, I can always do both, and use #4 to test those parts, and then 
> a simple mock user to test the rest.  It might make tests faster.

Exactly - use the most appropriate tool for the job to get coverage of 
the sections of your codebase which you care about, forget the rest. :)

Testing things like complex expiry policies etc with 'real' http 
requests is going to be a huge pain in the ass - I wouldn't even bother, 
and just whip up tests for that sort of stuff with Test::MockObject.

> The idea, though, of adding a controller at test-time, is brilliant.  If 
> it gets written down, will it be horribly abused?  =)

Probably, but as long as you're careful to point out that you're giving 
people a loaded gun, then it's their problem and we can stand back and 
laugh if they pull the trigger whilst it's aimed at any body parts.

> Where would this idea best be described?  Catalyst dev Wiki?  Blog entry?  
> Both?

Yes, both :)

> Thank you again for this rich response.

No problem, thank you for volunteering to expand and write down some of 
my rambling stream of consciousness..


More information about the Catalyst mailing list