Rails test - checking if user is signed in with Devise
Asked Answered
D

1

9

I am trying to test that someone is able to login to my site by making a POST request to my SessionsController. I've seen this way recommended in a few places:

it 'must be able to sign in a user' do
  user = create(:user)
  post :create, format: :js, user: {email: user.email, password: user.password, remember_me: 0}
  assert_response :success
  @controller.current_user.must_equal user
end

But this test is not correct. Calling @controller.current_user will attempt to authenticate the user using the posted parameters and will return user if the supplied email/password is correct. There is no guarantee that the create action is actually calling sign_in or current_user.

Even if I re-write the test to check that these methods are called, it's possible that other methods could be called e.g. sign_out.

Is there a more definitive way to ultimately check if a user is logged in, and if so, who the user is?

EDIT -

For example, the following test will pass

it 'must sign in a user' do
   @controller.current_user.must_equal nil
   post :create, format: :js, user: {email: @user.email, password: @user.password, remember_me: 0}
   assert_response :success
   @controller.current_user.must_equal @user
end

when the SessionsController#create action is:

def create
  respond_to do |format|
      format.js {
        render nothing: true, status: 200
      }
  end
end
Diogenes answered 24/8, 2016 at 11:16 Comment(5)
Can it help? #23794097Trilateral
I'm afraid I don't think that helps. I'm not having trouble calling current_user.Diogenes
is .current_user a method that attempts to log the user in, or is it an attr_reader for an instance variable? If the former, then it seems what you're describing is the expected behavior.Aircraftman
current_user is a method that will attempt to log the user in. If you run the test I outlined in the question with a completely empty controller action, the test will still passDiogenes
How you are handling sessions in controller? are you using rails session?Curator
C
2

Solution with minimal changes to proposed code in the question:

You need to initialize the system before the test starts. Try prepending following code before your it 'must be able to sign in a user' do code:

before (:each) do
  user = FactoryGirl.create(:user)
  sign_out user
end

This should turn your test into a valid test for your post controller.

Explanation:

My assumption is, that your test above always succeeds, because the user is already signed in (by other tests run before this one). You could verify this by using byebug in the line after it and run current_user in bybug's console. If it is not nil, the user is already signed in, which is invalidating your test.

Note, that (different from what is discussed above in the comments), current_user does not change the status of the user; it is a read-only function.

Shorter/cleaner solution:

In my opinion, there is a a cleaner way to perform such a test like follows:

def sign_in_via_post(user)
  post :create, format: :js, user: {email: user.email, password: user.password, remember_me: 0}
end

...

before (:each) do
  user = FactoryGirl.create(:user)
  sign_out user
end

it 'must be able to sign in a user' do
   { sign_in_via_post user }.should change { current_user }.from(nil).to(user)
end

With the should change from nil to user statement, you verify, that the user was logged out before the test begins and that the user is logged in, after the test has been performed.

Note, that the part

   { sign_in_via_post user }.should change { current_user }.from(nil).to(user)

is equivalent to the (maybe easier to understand) code

   { sign_in_via_post user }.should change { user_signed_in? }.from(false).to(true)

as discussed here.

Cogency answered 8/9, 2016 at 7:59 Comment(3)
Thanks for the response. I do believe that current_user can update the state of the user. I've added a sample test in my original question that I believe illustrates the issue.Diogenes
I just had a thought, maybe there is a before_action/filter that is doing something, but I can't check right now. Perhaps you are rightDiogenes
@user1063998: have you had a chance to test my suggestion? I have invested quite a bit of time to come up with the solution (at least I think, it is the solution). In any case, it was a good exercise, but I would prefer it to have it checked by the person who has asked.Cogency

© 2022 - 2024 — McMap. All rights reserved.