I’m wondering what pattern you use for testing authentication?
I currently test sign-in in a feature spec and then drop down to Controller specs to test access for each action.
So for each action of a controller that requires authentication I do something like:
describe ".new" do
context "authentication" do
it {
sign_in :user, create(:user)
get :new
should respond_with :success
}
it {
get :new
should redirect_to new_user_session_path
}
end
context "authenticated" do
before {
sign_in :user, create(:user)
get :new
}
it { should render_template :new }
# etc
end
end
I like this approach because if I inadvertently expose an action to unauthenticated users, I get immediate feedback. However I’m concerned that adding an authorisation layer (with multiple roles with different permissions would balloon the number of tests). I’m wondering what approach you take to testing authentication and authorisation?
I feel like this is tricky. Feature specs that sign a user in/out verify that the sign in functionality work, but they don’t verify that you are properly protecting all of your controller actions by requiring a login to access them. Testing every action in every controller spec would be a pain.
I generally take the approach of making all controller actions require a login by default by adding before_action :authorize to my ApplicationController.
I test drive this with application_controller_spec which has an inline anonymous controller that inherits from application controller. The anonymous controller has the 7 standard actions and I verify that I am required to be logged in for each of those actions to work:
require 'spec_helper'
describe ApplicationController do
describe 'authorization' do
controller do
def index; head :ok; end
def show; head :ok; end
def new; head :ok; end
def create; head :ok; end
def edit; head :ok; end
def update; head :ok; end
def destroy; head :ok; end
end
it { requires_signed_in_user_to { get :index } }
it { requires_signed_in_user_to { get :new } }
it { requires_signed_in_user_to { get :show, id: 1 } }
it { requires_signed_in_user_to { get :edit, id: 1 } }
it { requires_signed_in_user_to { put :update, id: 1 } }
it { requires_signed_in_user_to { post :create } }
it { requires_signed_in_user_to { delete :destroy, id: 1 } }
def requires_signed_in_user_to(&block)
yield
expect(response).to redirect_to(sign_in_path)
end
end
end
Then I test drive any actions that require skipping the login filter with dedicated controller or feature specs as appropriate.
This isn’t perfect - I could do skip_before_action :authorize somewhere where it wasn’t test-driven and it wouldn’t be caught, but that seems mighty negligent behavior. I could also have a controller that inherits from something that doesn’t have ApplicationController in its inheritence hierarchy, but this doesn’t come up very often.