Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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
like image 436
user1063998 Avatar asked Aug 24 '16 11:08

user1063998


People also ask

Can I use devise in rails 7?

You’re now able to run your app and it should include pages like /users/sign_up and /users/sign_in. In theory Devise should work now but, unfortunately, the view links and redirects used by Devise won’t work in Rails 7. Let’s fix that.

How do I create a user model in rails G?

Next, you’ll want to create your user model with devise, similar to the way you would create a user model otherwise. Run rails g devise user in your command line. This will create a user migration as well as the user model, which you can configure the same way you would any other model.

How to deal with Turbo in rails with devise?

We need to alter the code that Devise generates for us to deal with Turbo. So, once you’ve run rails generate devise:install we need to alter the Devise initializer config in several places beyond what the Devise README instructs us to do and add a controller as Devise’s parent controller.

Does integrating devise Auth with rails 7 have any undocumented twists?

Integrating Devise Auth with Rails 7 has some undocumented twists. Learn about them here or use the included template to bypass them


1 Answers

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.

like image 112
Olli Avatar answered Sep 29 '22 11:09

Olli