Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use mock models in AuthLogic controller specs?

I am trying to write specs for a controller without using fixtures (instead employing mock models). This controller requires a user to be logged in, for which I'm employing AuthLogic, following the author's recommendations.

describe UsersController do

  def mock_user(stubs={})
    @mock_user ||= mock_model(User, stubs)
  end

  context 'when logged in' do
    before { activate_authlogic }

    it "exposes the logged-in user as @user in response to GET (show)" do
      UserSession.create(mock_user)
      ...
    end

    ...
  end

  ...
end

These examples all fail at the line UserSession.create(...), reporting to the effect of:

Mock 'User_1005' received unexpected message :changed? with (no args)

I'm not sure how to resolve this; is mocking with :changed? => false appropriate?

like image 821
Andres Jaan Tack Avatar asked Feb 28 '23 14:02

Andres Jaan Tack


2 Answers

Iain posted a solution to using mock objects with AuthLogic. To rephrase, the following helpers go into spec_helpers.rb:

def current_user(stubs = {})
  @current_user ||= mock_model(User, stubs)
end

def user_session(stubs = {}, user_stubs = {})
  @current_user_session ||= mock_model(UserSession, {:user => current_user(user_stubs)}.merge(stubs))
end

def login(session_stubs = {}, user_stubs = {})
  UserSession.stub!(:find).and_return(user_session(session_stubs, user_stubs))
end

def logout
  @user_session = nil
end

I've incorporated this into my specs, and I find it does exactly what I was hoping. I have working controller specs that exploy mock models for the logged-in user, so now they don't all break when I add a field to User. Iain's example of implementing this in a spec is as:

describe SecretsController do
  before { login }
  it "should be very very secret!"
end

P.S. I hate to answer my own question, but this is the answer I was looking for; I just didn't find it early enough.

like image 183
Andres Jaan Tack Avatar answered Mar 12 '23 09:03

Andres Jaan Tack


Authlogic expects the record to acts like an active record instance. You can use a real instance or a mock, but if you use a mock/stub you must be sure it responds to all the methods required by Authlogic.

I would suggest to use a real active record object instead of a mock. If you don't want to use a fixture, you can use a Factory.

The last option would be to pass a mock that responds to any methods (you can easily accomplish this via method_missing). The problem with that solution is that you don't know in advance which value should return any specific method call.

Yes, you can pass false but this is not really a solution. It would require to manually try/add a default value until you find the mock object answering all the Authlogic request. But this would require you to constantly follow authlogic for any internal change to fix unanswered calls to your stub.

like image 38
Simone Carletti Avatar answered Mar 12 '23 07:03

Simone Carletti