A.M.
A.M.

Reputation: 1797

NoMethodError: undefined method `remember_token' for nil:NilClass

I have seen this particular error (or question/topic) in many other places. However, none of the solutions have solved my problem. Regarding the chapter 8 of Michael Hartl book, I have added the following integration test:

test "login with remembering" do
   log_in_as(@user, remember_me: '1')
   assert_not_nil cookies['remember_token']
   assert_equal cookies['remember_token'], assigns(:user).remember_token
end

However, the last assert_equal gives me the error I have put in the title.

What I have done so far:

Suggested by other questions, I have generated another migration adding remember_token to the User model. However, this did not solve the problem.

How can I solve this?

Upvotes: 1

Views: 1806

Answers (5)

therealrodk
therealrodk

Reputation: 434

I had this same problem and was able to solve it thanks to Stefan Muntwyler's answer. What happened to me (and probably to the poster, as well) is that the exercise that required you to make the user variable into an instance variable (@user) was later overridden by some updated code that removed the instance variable you created.

To fix this, change this code in sessions_controller.rb:

def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      if user.activated?
        log_in user
        params[:session][:remember_me] == '1' ? remember(user) : forget(user)
        redirect_back_or user
      else
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

To this:

def create
    @user = User.find_by(email: params[:session][:email].downcase)
    if @user && @user.authenticate(params[:session][:password])
      if @user.activated?
        log_in @user
        params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
        redirect_back_or @user
      else
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

Upvotes: 0

Stefan Muntwyler
Stefan Muntwyler

Reputation: 428

Check your sessions_controller. It should use an instance variable (@user) within the create method, anywhere you reference the user.

def create
  @user = User.find_by(email: params[:session][:email].downcase)
  .
  .
  .
end

Rails Tutorial - Chapter 9.26 Exercises 1)

Inside a test, you can access instance variables defined in the controller by using assigns with the corresponding symbol. For example, if the create action defines an @user variable, we can access it in the test using assigns(:user).

Changing the create method to use an instance variable was left as an exercise near Chapter 9.26. But the users_login_test "login with remembering" method relies on assigns(:user) to access the remember_token.

  test "login with remembering" do
    log_in_as(@user, remember_me: '1')
    assert_equal assigns(:user).remember_token, cookies['remember_token']
    assert_not_empty cookies['remember_token']
  end

Upvotes: 1

Jack Barry
Jack Barry

Reputation: 310

Without seeing the full error message, it's hard to know exactly where the problem is, but when I ran into this one it was because I had forgotten to save my sessions_controller.rb before running the test.

Here is the exact error I received:

ERROR["test_login_with_remembering", UsersLoginTest, 2016-03-03 20:00:56 -0600]
 test_login_with_remembering#UsersLoginTest (1457056856.16s)
NoMethodError:         NoMethodError: undefined method `remember_token' for nil:NilClass
        test/integration/users_login_test.rb:42:in `block in <class:UsersLoginTest>'
    test/integration/users_login_test.rb:42:in `block in <class:UsersLoginTest>'

The following is my create method inside of the sessions controller:

def create
  @user = User.find_by(email: params[:session][:email].downcase)
  if @user && @user.authenticate(params[:session][:password])
    log_in @user
    params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
    redirect_to @user
  else
    flash.now[:danger] = 'Invalid email/password combination'
    render 'new'
  end
end

Upvotes: 1

chumakoff
chumakoff

Reputation: 7044

In your test the assigns(:user) instance does not exists. You are using the @user instance in log_in_as method. Why are you not using it in your last assert_equal method ? Try this

assert_equal cookies['remember_token'], @user.remember_token

I think it will work.

And find out why you are using assigns(:user) and why it is nil.

Edited:

If it will not work then first try to reload the user data before you make the assertion:

@user.reload
assert_equal cookies['remember_token'], @user.remember_token

And I would add one more assertion to your test that checks if the user has 'remember_token' after he logged in. It will help you to detect the wrong place in your code. If this assertion will fail then you will make sure that there is something wrong in your authorization process.

test "login with remembering" do
   log_in_as(@user, remember_me: '1')
   @user.reload
   assert_not_nil @user.remember_token
   assert_not_nil cookies['remember_token']
   assert_equal cookies['remember_token'], @user.remember_token
end

Upvotes: 0

Simone Carletti
Simone Carletti

Reputation: 176472

The problem there is probably not that the User model has no remember_token attribute, rather that the value of assigns(:user) is nil, which means your controller doesn't properly set the @user attribute.

Upvotes: 1

Related Questions