Mar 2010

Rails: RSpec'ing controllers with declarative authorization AND AuthLogic

I just had a rough time figuring out how to bypass all the security features of the Rails project I am developing to write decent controller specs with RSpec. I am using AuthLogic as authentication module and declarative authorization (DA) for exactly that. However, when I started to write controller specs that would simulate (HTTP) GET requests, I ran into a wall: I simply could not digg what the cleanest way would be to bypass both AuthLogic and DA. Finally, after finding the right queries in Google, I managed to get the necessary snippets. To avoid that the same tedious task might befall you, here's what you need to add, e.g., to your spec_helpers directory - I called the file "controller_helpers.rb":

module SessionHelper
  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

  def disable_authorization()
    Authorization.ignore_access_control(true)
  end
end

The trick is that, for AuthLogic, you can now "authenticate" the user by the stubbed UserSession that returns a mocked User model. DA is less complicated: the disable_authorization() method is all that is needed. Now, in your "spec_helper.rb", you add this line to the top:

require File.dirname(__FILE__) + '/spec_helpers/controller_helpers'

And this line somewhere in the Spec::Runner.configure loop:

config.include(SessionHelper)

Now, in your controller specs, it is more than trivial to disable authorization and authentication at once - simply add the following line, e.g., to your before(:each) definitions:

disable_authorization && login

Voila - your GET requests pass; and you can even add stubs to your User model, if needed, by adding them as key-value pairs to the login() call above! So now you can get back to make your specs pass...