0
votes

I am following Lesson 9 for sign-in, yet the signin test just won't pass! I followed the code to the letter and it still wont pass.

when testing with the browser - the sign in works with no errors..

Failures:

1) SessionsController POST 'create' with valid email and password should sign the user in Failure/Error: controller.current_user.should == @user expected: # got: nil (using ==) # ./spec/controllers/sessions_controller_spec.rb:55:in `block (4 levels) in '

2) SessionsController POST 'create' with valid email and password should redirect to the user show page Failure/Error: response.should redirect_to(user_path(@user)) Expected block to return true value. # ./spec/controllers/sessions_controller_spec.rb:61:in `block (4 levels) in '

Finished in 5.12 seconds 7 examples, 2 failures

Failed examples:

rspec ./spec/controllers/sessions_controller_spec.rb:53 # SessionsController POST 'create' with valid email and password should sign the user in
rspec ./spec/controllers/sessions_controller_spec.rb:59 # SessionsController POST 'create' with valid email and password should redirect to the user show page

As you can see, the test controller.current_user.should == @user returns nil for some reason.

Please help me understand this..

Thank you

SessionHelper

    module SessionsHelper

 def sign_in(user)
    cookies.permanent.signed[:remember_token] = [user.id, user.salt]
    self.current_user= user
     end


def signed_in?
    !self.current_user.nil?
end
def current_user=(user)
    @current_user = user
end

 def current_user
    @current_user ||= user_from_remember_token
end

  private

def user_from_remember_token
  User.authenticate_with_salt(*remember_token)
end

def remember_token
  cookies.signed[:remember_token] || [nil, nil]
end
   end

Session controller

    class SessionsController < ApplicationController
     def new
       @title = "Sign In"
       end

    def create
    user = User.authenticate(params[:session][:email], params[:session][:password])
    if user.nil?
        flash.now[:error] = "Invalid email/password"

        @title = "Sign In (right this time)"
        render 'new'
    else
        #sign in the user
        sign_in @user
        redirect_to user

    end
end

def destroy

end
   end

User Model

   require 'digest'
    class User < ActiveRecord::Base

attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation


email_regex =  /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i



validates :name, :presence => true,
                        :length => {:minimum => 2, :maximum => 50}
validates :email, :presence => true, :format => {:with => email_regex}, :uniqueness => {:case_sensitive => false }

#Automatically creates the virtual password confimration attribute
validates :password, :presence => true,
                                :confirmation => true,
                                :length => {:within => 6..40}


before_save :encrypt_password

def has_password?(submitted_password)
    self.salt = make_salt if new_record?
    encrypted_password == encrypt(submitted_password)
end

def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    return nil if user.nil?
    return nil if user.has_password?(submitted_password)
end

 def self.authenticate_with_salt(id, cookie_salt)
    user = find_by_id(id)
    (user && user.salt == cookie_salt) ? user : nil
end


     private

def encrypt_password
  self.salt = make_salt unless has_password?(password)
  self.encrypted_password = encrypt(password)
end

def encrypt(string)
  secure_hash("#{salt}--#{string}")
end

def make_salt
  secure_hash("#{Time.now.utc}--#{password}")
end

def secure_hash(string)
  Digest::SHA2.hexdigest(string)
end
   end

session controller spec

require 'spec_helper'

describe SessionsController do
render_views


  describe "GET 'new'" do
    it "should be successful" do
      get 'new'
      response.should be_success
    end

    it "should have the right title" do
        get :new
        response.should have_selector("title", :content => "Sign In")
    end



  end

  describe "POST 'create'" do

        describe "invalid login" do
            before(:each) do
                @attr = {:email => "[email protected]", :password => "invalid"}
            end

            it "should re-render the new page" do
                post :create, :session => @attr
                response.should render_template('new')
            end

            it "should have the right title" do
                post :create, :session => @attr
                response.should have_selector("title", :content => "Sign In (right this time)")
            end

            it "should have flash.now message" do
                post :create, :session => @attr
                flash.now[:error].should =~ /invalid/i
            end
        end

        describe "success" do

            before(:each) do
                @user = Factory(:user)
                @attr = { :email => @user.email, :password => @user.password }
            end

            it "should sign the user in" do
                post :create, :session => @attr
                controller.current_user.should == @user
                controller.should be_signed_in
            end

            it "should redirect to the user show page" do
                post :create, :session => @attr
                response.should redirect_to(user_path(@user))
            end
        end

    end

end
3
I suggest you post as much of your code as possible - your test, model, session_controller, session_helper etc.Michael De Silva

3 Answers

3
votes

I'm assuming you've completed past Section 9.3.3 and added the authenticate class method to your user model.

If you check your authenticate (9.17) class method it has user = find_by_email(email). You should check that your factory is in fact creating a user in your test database first.

Update

Do you have this in SessionsHelper

def signed_in?
  !current_user.nil?
end

Update 2

Remove the following in your SessionsHelper!!

def current_user=(user)
    @current_user = user
end

Hartl is quite specific as to why this writer is removed

If we did this, we would effectively replicate the functionality of attr_accessor, first seen in Section 4.4.5 and used to make the virtual password attribute in Section 7.1.1.7 The problem is that it utterly fails to solve our problem: with the code in Listing 9.15, the user’s signin status would be forgotten: as soon as the user went to another page—poof!—the session would end and the user would be automatically signed out.

To avoid this problem, we can find the session user corresponding to the cookie created by the code in Listing 9.12, as shown in Listing 9.16.

It is instead replaced by

def current_user
  @current_user ||= user_from_remember_token
end

First, Listing 9.16 uses the common but initially obscure ||= (“or equals”) assignment operator (Box 9.4). Its effect is to set the @current_user instance variable to the user corresponding to the remember token, but only if @current_user is undefined.8 In other words, the construction calls the user_from_remember_token method the first time current_user is called, but on subsequent invocations returns @current_user without calling user_from_remember_token.

1
votes

I had the same issue. I understood also that it should be replaced. But it should not both methods are needed.

def current_user=(user)
  @current_user = user
end

def current_user
  @current_user ||= user_from_remember_token
end 
0
votes

Took me forever to track this one down...

The rspec error message:

 1) SessionsController POST 'create' with valid email and password should sign the user in
     Failure/Error: controller.current_user.should == @user
     NoMethodError:
       undefined method `current_user' for #<SessionsController:0x00000005426eb8>
     # ./spec/controllers/sessions_controller_spec.rb:51:in `block (4 levels) in <top (required)>'

You want to edit app/helpers/sessions_helper.rb :

WRONG:

def current_user=(user)
    @current_user = user ||= user_from_remember_token
end

RIGHT:

def current_user
    @current_user ||= user_from_remember_token
end

I clearly wasn't paying close enough attention when I was following along and unfortunately the error message from rspec wasn't as helpful as it normally is. Hope this helps!