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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With