Functional tests and dealing with login before_filter


#1

Hi all,

I’m curious as to how you do functional testing on the controllers if,
within
the ApplicationController, I have a before_filter :authorize, :except =>
:login, where the private authorize method checks for session[:user].

I can’t do “post :login”, because that method is in a different
controller.

I tried setting session[:user] directly in the setup method, but that
gave me
strange errors:

fixtures :regions
def setup

# I tried a few different things here…
session[:user] = { :attributes => {:user => “foo”} }
end

TypeError: Symbol as array index
regions_controller_test.rb:16:in []=' regions_controller_test.rb:16:insetup_without_fixtures’

/opt/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/fixtures.rb:523:in
`setup’

Any advice appreciated.

Thanks,

Dan


#2

On Jan 30, 2006, at 9:09 AM, Daniel B. wrote:

fixtures :regions
def setup

I tried a few different things here…

session[:user] = { :attributes => {:user => “foo”} }

For whatever reason the session object only works after a request,
not before. Its totally lame.

I use a util_set_user method since some of my actions should behave
differently for logged in and logged out people:

def util_set_user(user)
@request.session[:userid] = user.id
end

But you can also set the session on the third argument to get/post:

def process(action, parameters = nil, session = nil, flash = nil)

from actionpack/action_controller/test_process.rb

Wonderfully, its not documented in the api documentation as you would
expect, but instead is buried in a manual under the unhelpful title
“Testing Your Controllers: An Anatomy Lesson”.

http://manuals.rubyonrails.com/read/chapter/28#page72


Eric H. - removed_email_address@domain.invalid - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com


#3

On Mon, Jan 30, 2006 at 10:09:18AM -0700, Daniel B. wrote:

I can’t do “post :login”, because that method is in a different controller.

Yeah, that’s a bit sucky. I handled it with this tasteless hack
(wrapped in
a login method in test_helper.rb):

realctl = @controller
@controller = LoginController.new
post :index, { 'loginname' => user, 'password' => pass }
@controller = realctl

But it leaves me with some grave reservations. A way to post to a
different
controller would be ideal, but I don’t think

I kinda like Eric’s suggestion of passing the session in manually, but
that’s icky in a couple of different ways:

  • You’ve got a fair chunk of code you’ll (but you can work around that
    with
    helpers);

  • You’re tying your implementation of logging in in your tests (setting
    a
    particular session value) to the mechanics of login detection far closer
    than is comfortable.

It’s definitely a thorny issue, though.

  • Matt

#4

On Jan 31, 2006, at 11:18 , Matthew P. wrote:

I kinda like Eric’s suggestion of passing the session in manually, but
that’s icky in a couple of different ways:

  • You’ve got a fair chunk of code you’ll (but you can work around
    that with
    helpers);

I added a small method in test_helper.rb to set the session for me,
passing it the member (user) that is supposed to be logged in.

def set_session_for(member)
@request.session[:member_id] = member.id
end

I then can call set_session_for in my test methods to have them
logged in. Works well for me.

  • You’re tying your implementation of logging in in your tests
    (setting a
    particular session value) to the mechanics of login detection far
    closer
    than is comfortable.

I rationalize this by testing logging by thoroughly testing my
login_controller. And the before_filter is just checking for that
particular session value to be set anyway (if it’s similar to the
examples in AWDwR). Perhaps you could abstract this out a bit by
naming the helper method log_in(member) and then keep the code that
does sets up the log in (in this case setting a session parameter) in
the log_in helper method. Then you only have to change the guts of
one method if your log in implementation changes.

Michael G.
grzm myrealbox com


#5

On Jan 30, 2006, at 7:22 PM, Michael G. wrote:

I added a small method in test_helper.rb to set the session for me,
passing it the member (user) that is supposed to be logged in.

def set_session_for(member)
@request.session[:member_id] = member.id
end

I then can call set_session_for in my test methods to have them
logged in. Works well for me.

Yes, this is what I do. One line isn’t too much.

naming the helper method log_in(member) and then keep the code that
does sets up the log in (in this case setting a session parameter)
in the log_in helper method. Then you only have to change the guts
of one method if your log in implementation changes.

Yes. By properly abstracting (set_session_for) and properly testing
the login code you will get a clear indication why all your logged-in
tests are failing.

This way if you change the login code all your tests requiring a
valid session will fail but you only have to fix your tests in one
place. (If you’re constantly changing how valid sessions are
represented you’re doing something very wrong.)

You don’t want to test your login code for every logged-in action,
that’s why you wrote a test for your filters. I create a test-only
DummyController for testing login and other filters which gives me a
clear indication what is broken with which filter.

When I’m testing an action that requires the user to be logged in I
want to test the the action, not that the login filter works.

PS: If you can’t figure out what part of your login system is broken
from your tests you obviously don’t have enough tests.


Eric H. - removed_email_address@domain.invalid - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com