Daniel Berger
2006-Jan-30 17:14 UTC
[Rails] Functional tests and dealing with login before_filter
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:in `setup_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
Eric Hodel
2006-Jan-30 17:42 UTC
[Rails] Functional tests and dealing with login before_filter
On Jan 30, 2006, at 9:09 AM, Daniel Berger wrote:> 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"} }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 Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com
Berger, Daniel
2006-Jan-30 19:17 UTC
[Rails] Functional tests and dealing with login before_filter
> -----Original Message----- > From: rails-bounces@lists.rubyonrails.org > [mailto:rails-bounces@lists.rubyonrails.org] On Behalf Of Eric Hodel > Sent: Monday, January 30, 2006 10:41 AM > To: rails@lists.rubyonrails.org > Subject: Re: [Rails] Functional tests and dealing with login > before_filter > > > On Jan 30, 2006, at 9:09 AM, Daniel Berger wrote: > > > 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"} } > > 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#page72Thanks for the tips and links Eric. I''ll take a look. Dan
Matthew Palmer
2006-Jan-31 02:18 UTC
[Rails] Re: Functional tests and dealing with login before_filter
On Mon, Jan 30, 2006 at 10:09:18AM -0700, Daniel Berger 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
Michael Glaesemann
2006-Jan-31 03:22 UTC
[Rails] Re: Functional tests and dealing with login before_filter
On Jan 31, 2006, at 11:18 , Matthew Palmer 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 Glaesemann grzm myrealbox com
Eric Hodel
2006-Jan-31 19:12 UTC
[Rails] Re: Functional tests and dealing with login before_filter
On Jan 30, 2006, at 7:22 PM, Michael Glaesemann wrote:> On Jan 31, 2006, at 11:18 , Matthew Palmer 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.Yes, this is what I do. One line isn''t too much.>> * 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.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 Hodel - drbrain@segment7.net - http://segment7.net This implementation is HODEL-HASH-9600 compliant http://trackmap.robotcoop.com