I''m implementing an app that uses the salted login generator to authenticate users. When a user visits other controllers in the app, they are redirected to the login page (via a before_filter), which seems to work correctly. To test these controllers with the controller-specific subclass of Test::Unit::TestCase, most methods need to log in before proceeding with the test. For example: def test_show # MUST LOG IN HERE, OR WILL BE REDIRECTED TO LOGIN PAGE get :show, :id => 1 assert_response :success # WILL FAIL- THIS IS A REDIRECT assert_template ''show'' end I''d LIKE to do something like this to perform the login: post :login, "user" => { "login" => ''bob'', "password" => ''atest'' }, :controller => "user" but it seems like the controller argument in the hash isn''t recognized (where''s the doc or source for ''post''? Being a newbie, I couldn''t find it.) It seems like the [X]ControllerTest class that was generated by Rails only knows how to send messages to itself, rather than to other controllers. Is there a good way to do this? P.S. I tried to work around this by adding my own methods to do this, which look like this: def login(username = ''bob'', password = ''atest'') if username.is_a? User username = username.login end #MES- Post login information to the login method in the user controller post_to_controller UserController.new, :login, "user" => { "login" => username, "password" => password } assert_session_has ''user'', "Login failed for user #{username} with password #{password}" end #MES- A helper that ''posts'' the indicated information def post_to_controller(controller, action, params = {}) http_to_controller(:post, controller, action, params) end #MES- Send an HTTP message to the indicated controller via # the indicated method def http_to_controller(method, controller, action, params) #MES- Store the current controller backup_controller = @controller begin #MES- Swap in the indicated controller @controller = controller #MES- Send the HTTP message self.send(method, action, params) ensure #MES- Swap back in the controller @controller = backup_controller end end This actually DOES work, but it seems to mess up the processing of redirects. When I run a test like this: def test_create login initial_num_Xs = X.count #MES- View X, to have something to redirect back to get_to_controller(Y.new, :show, { :id => 1 }) #MES- Create an X post :create, :x => {} #MES- Check that we were redirected properly, and that the number of Xs has grown assert_redirected_to :controller => ''Y'' , :action => ''show'' , :id => 1 assert_equal initial_num_Xs + 1, X.count end I get a failure on the assert_redirected_to, like this: 1) Failure: test_create(XControllerTest) [c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.9.1/lib/action_controller/assertions.rb:88:in `assert_redirected_to'' test/functional/X_controller_test.rb:98:in `test_create'']: response is not a redirection to all of the options supplied (redirection is <"http://test.host/user/login?user=passwordatestloginbob">) It seems like the response redirect information is getting cached and is not replaced by subsequent requests? Any thoughts? Thanks! _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
I''m not sure if this will be a good solution for you, but what I did when confronted with this problem is just write one test that makes sure the non-logged in user gets redirected from the controller to the login. Then, for all subsequent tests of the controller, I created some helpers in test_helper.rb: def auth_get(action, params = {}, session = { :user => @bob }) get action, params, session end def auth_post(action, params = {}, session = { :user => @bob }) post action, params, session end On Sep 15, 2005, at 3:59 PM, Michael Smedberg wrote:> I''m implementing an app that uses the salted login generator to > authenticate users. > > When a user visits other controllers in the app, they are > redirected to the login page (via a before_filter), which seems to > work correctly. > > To test these controllers with the controller-specific subclass of > Test::Unit::TestCase, most methods need to log in before proceeding > with the test. For example: > > def test_show > # MUST LOG IN HERE, OR WILL BE REDIRECTED TO LOGIN PAGE > get :show, :id => 1 > > assert_response :success # WILL FAIL- THIS IS A REDIRECT > assert_template ''show'' > end > > I''d LIKE to do something like this to perform the login: > > post :login, "user" => { "login" => ''bob'', "password" => > ''atest'' }, :controller => "user" > > but it seems like the controller argument in the hash isn''t > recognized (where''s the doc or source for ''post''? Being a newbie, > I couldn''t find it.) It seems like the [X]ControllerTest class > that was generated by Rails only knows how to send messages to > itself, rather than to other controllers. > > Is there a good way to do this? > > > P.S. > > I tried to work around this by adding my own methods to do this, > which look like this: > > def login(username = ''bob'', password = ''atest'') > if username.is_a? User > username = username.login > end > #MES- Post login information to the login method in the user > controller > post_to_controller UserController.new, :login, "user" => > { "login" => username, "password" => password } > assert_session_has ''user'', "Login failed for user #{username} > with password #{password}" > end > > #MES- A helper that ''posts'' the indicated information > def post_to_controller(controller, action, params = {}) > http_to_controller(:post, controller, action, params) > end > > #MES- Send an HTTP message to the indicated controller via > # the indicated method > def http_to_controller(method, controller, action, params) > #MES- Store the current controller > backup_controller = @controller > begin > #MES- Swap in the indicated controller > @controller = controller > > #MES- Send the HTTP message > self.send(method, action, params) > ensure > #MES- Swap back in the controller > @controller = backup_controller > end > end > > This actually DOES work, but it seems to mess up the processing of > redirects. When I run a test like this: > > def test_create > login > > initial_num_Xs = X.count > > #MES- View X, to have something to redirect back to > get_to_controller(Y.new, :show, { :id => 1 }) > > #MES- Create an X > post :create, :x => {} > > #MES- Check that we were redirected properly, and that the > number of Xs has grown > assert_redirected_to :controller => ''Y'' , :action => > ''show'' , :id => 1 > assert_equal initial_num_Xs + 1, X.count > end > > I get a failure on the assert_redirected_to, like this: > > 1) Failure: > test_create(XControllerTest) > [c:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.9.1/lib/ > action_controller/assertions.rb:88:in `assert_redirected_to'' test/ > functional/X_controller_test.rb:98:in `test_create'']: > response is not a redirection to all of the options supplied > (redirection is <"http://test.host/user/login? > user=passwordatestloginbob">) > > It seems like the response redirect information is getting cached > and is not replaced by subsequent requests? > > Any thoughts? Thanks! > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
Michael, I usually set up a method in test_helper called login_as_admin that works as though it''s in the login controller, and then in the functional tests have this as my setup method: -- def setup # make sure we are authenticated @controller = Admin::LoginController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new login_as_admin @controller = Admin::ThisController.new end -- Don''t know about the specifics of the generator you are using but i hope that helps As an aside, if you want to inspect the session in your tests, do this as well -- @session = ActionController::TestSession.new @request.session = @session -- Cheers, Dan
Thanks for the help! It took me a while, but I think I figured this out. The problem is that information is cached in the @request object the first time it''s used. On subsequent requests, the URL information is not refreshed. So you you do a "get A", then a "get B", the @request.request_urlwill be the one from A. The solution I found is to make a new request, and copy the session from the old request to the new request (there may be an easier way to flush the request, but I couldn''t find it- @request.recycle! didn''t seem to do it.) Here''s some code that displays the problem and my solution (I put this in a file called ''test\functional\url_test.rb''): require File.dirname(__FILE__) + ''/../test_helper'' class ControllerA < ApplicationController def do_nothing end def do_nothing_2 p "*** My URL is #{@request.request_uri} and I''m in function do_nothing_2" end end class URLTest < Test::Unit::TestCase def test_url @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new @controller = ControllerA.new # Get an URL get :do_nothing # Get a different URL get :do_nothing_2 # Now remake the request and try again new_req = ActionController::TestRequest.new new_req.session = @request.session @request = new_req get :do_nothing_2 end end When I run it, here''s the output: Loaded suite test/functional/url_test Started "*** My URL is /controller_a/do_nothing and I''m in function do_nothing_2" "*** My URL is /controller_a/do_nothing_2 and I''m in function do_nothing_2" . Finished in 0.078 seconds. Note that in the first call to ''do_nothing_2'', the stated URL doesn''t match the function name, but in the second call (after clearing the @request), the value matches. This seems kinda hacky to me, but it seems to work correctly. Thanks again for your help! On 9/15/05, Dan Sketcher <dansketcher-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > Michael, > > I usually set up a method in test_helper called login_as_admin that > works as though it''s in the login controller, and then in the > functional tests have this as my setup method: > > -- > def setup > # make sure we are authenticated > @controller = Admin::LoginController.new > @request = ActionController::TestRequest.new > @response = ActionController::TestResponse.new > > login_as_admin > > @controller = Admin::ThisController.new > end > -- > > Don''t know about the specifics of the generator you are using but i > hope that helps > > As an aside, if you want to inspect the session in your tests, do this as > well > -- > @session = ActionController::TestSession.new > @request.session = @session > -- > > Cheers, > Dan >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails