Testing Actions Requiring A Login

Hello,

I would be grateful if somebody could let me know how to test actions on a controller which are intercepted by a before_filter requiring a login. The catch is that the before_filter is on my application controller and I am trying to test my users controller.

I have functional tests for the login and logout actions on their own controller. Now I just want to be able to post a valid username and password into that controller, from my users controller, so I can just get on with testing my users controller's actions.

Any help would be much appreciated.

Thanks and regards,
Andy Stewart

In a functional test you can simply populate the session… the functional test really should only test the method itself, not the login. It’s common practice to put the user’s id in the session… so in a functional test we can just populate the session… In this example, I am assuming that session[:user_id] will be 1 (the id of my logged in user).

def test_edit_user

passing the session should get me there

get :edit, {:id=>1}, {:user_id => 1}

assert_response :success

end

def test_edit_user_without_login

no session? should get denied

get :edit, {:id=>1}

assert_response :redirect

assert flash[:notice] = “Please log in.”

end

Testhing the integration of logins with other actions can be done using Integration tests

def test_edit_user

get “/user/edit/1” #gets the edit page

assert_response :redirect # should be redirected to the login page

follow_redirect #follow that redirect

post “/main/login” , {:username=>“homer”, :password=>“simpson”} # post username and password

assert_response :redirect # login should redirect you back to the url they wanted to go to originally

follow_redirect # follow that redirect

assert_response :success # Was it successful?

assert_template “edit” # was it the edit page??

end

My feeling is that functional tests are meant to act as unit tests for a controller. You don’t want any dependencies, you just want to test their behavior. Integration tests should test the dependencies.

Does that help?

the functional test really should only test the method itself, not the login.

Good -- that's what I would like to do.

It's common practice to put the user's id in the session... so in a functional test we can just populate the session... In this example, I am assuming that session[:user_id] will be 1 (the id of my logged in user).

That works well for me. Stuffing the user_id into the session successfully passes my login filter.

def test_edit_user
    # passing the session should get me there
    get :edit, {:id=>1}, {:user_id => 1}
    assert_response :success
end

Aha! I didn't know that passing a second hash set up the session; that's good to learn. (I can't find it in the API docs though?)

Testhing the integration of logins with other actions can be done using Integration tests

Oh, okay. So a functional test can only test one controller and integration tests are for spanning multiple controllers.

My feeling is that functional tests are meant to act as unit tests for a controller. You don't want any dependencies, you just want to test their behavior. Integration tests should test the dependencies.

That confirms what I thought. I just wasn't quite sure how to bypass the dependencies for the functional tests.

Does that help?

Yes it does, Brian, thank you. You've taught me a new way to pass session parameters and confirmed the way to proceed.

Thanks again,
Andy

Alan, thanks for the great link. That will be very helpful.

Regards,
Andy

Ahh, It seems to wordy for me having to specify it in every request.
I use following technique:

  1. Have login test helper with login_as and logout methods. Those methods set appropriate @
    request.session values.
  2. When I just need to run functional tests assuming that user is logged in, I do login_as :some_user in my test’s setup method:

class FooControllerTest < Test::Unit::TestCase
fixtures :user, :roles, :rights, :rights_roles, :roles_users

def setup
@request = …
@response = …
@controller = …

login_as :admin_user

end


end

  1. You can also add some sugar:

class Test::Unit::TestCase

def self.include_user_fixtures
fixtures :user, :roles, :rights, :rights_roles, :roles_users
end
end

and then write

class FooControllerTest < Test::Unit::TestCase

include_user_fixtures

end

or you can add some parent class for all controller test (assuming that in every controller you should deal with user permissions):

class ControllerTest < Test::Unit::TestCase
include_user_fixtures

end

class FooControllerTest < ControllerTest
end

Hope that helps.

1. Have login test helper with login_as and logout methods. Those methods set appropriate @ request.session values.
2. When I just need to run functional tests assuming that user is logged in, I do login_as :some_user in my test's setup method:

[snip]

3. You can also add some sugar:

[snip]

That's all good stuff -- thank you. I'll make use of it.

Regards,
Andy Stewart