views:

102

answers:

2

Hi,

I am having trouble mocking authlogic from shoulda.

I have the following test fixture:

class HomeControllerTest < ActionController::TestCase
  context "on GET to index" do
    setup do
      activate_authlogic
      UserSession.stubs(:current_user).returns( user_session )
      get :index
    end

    should respond_with :success
  end

  def user_session
    @user_session.stubs(:user).returns(Factory.build(:user))
    @user_session.stubs(:record)
    @user_session
  end
end

The problem is that the require_user method in ApplicationController is still not getting a current_user.

def current_user
  puts "#{defined?(@current_user)}"
  return @current_user if defined?(@current_user)
  @current_user = current_user_session && current_user_session.record
end

def require_user
  unless current_user
    store_location
    flash[:notice] = "You must be logged in to access this page"
    redirect_to login_url
    return false
  end
end

Can anyone see what is wrong?

Cheers

Paul

A: 

I think the problem may be something in the final line in your current_user method.

You might want to split it down into a few lines to make it more readable and easier to diagnose which of the parts of that statement are not as you'd expect in your test.

E.g.

@current_user = current_user_session
@current_user.record if @current_user
return @current_user
Shadwell
Chris
Fair enough, but I still think that expanding it out into more lines will make it easier to figure out what is going wrong in the test. I've updated my answer to remove the incorrect return value assumption.
Shadwell
Also, is the return value of the `current_user` method not the value of `current_user_session.record` ? Which if something is going wrong with the stubbing may well be `nil`.
Shadwell
Chris
Check out http://github.com/binarylogic/authlogic_example/blob/master/app/controllers/application_controller.rb To see a bare-bones implementation of AuthLogic in the ApplicationController.
Chris
Right, gottit, I think :) Thanks for the explanation.
Shadwell
@shadwell is correct, stubbing current_user_session.record to return a user does indeed work.
dagda1
A: 

It looks like your mock is a little confused; It's mocking a User object (but the instance variable you're using is called @user_session), and then stubbing a #record method on that user.

Also, you're mocking the current_user method on the UserSession controller, when it's actually defined on ApplicationController.

What you're trying to assert in your test is that a user must be logged in to access the Home#index page. In this test, you shouldn't focus on the UserSession object -- it's not as important. What is important is that if the system recognizes a user object has been retrieved and identified as the "current_user", they should be allowed to access the page.

The following spec might work out for you:

class HomeControllerTest < ActionController::TestCase
  context "on GET to index" do
    setup do
      activate_authlogic
      @user = Factory.build(:user)
      login_as_user(@user)

      @controller.should_receive(:current_user).and_return(@user)

      get :index
    end

    should respond_with :success
  end

  def login_as_user(user = Factory.build(:user))
    @controller.stubs(:current_user).returns(user)
  end
end

I'm using the @controller variable that ActionController::TestCase makes available to you. It represents the instance of the current controller being tested.

An important thing to note here is that -- with the exception of line 4 -- this test now doesn't care what you're using to provide authentication functionality. So long as your application defines a current_user method that returns a User object or nil, you could be using AuthLogic, Devise or any other solution, and your test will continue to work as you might expect.

Chris