简体   繁体   中英

Rails test - checking if user is signed in with Devise

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 eg 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

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 i t '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 aa 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM