Ivan Tkalin

I’m a software engineer. I solve problems.

Testing authorization using RSpec parametrized shared examples

I have a rails app, that uses Devise for authentication and CanCan for authorization. There is a group of controllers in my application, that should be accessible by administrators only. I was thinking about a DRY way to test these resources and their accessibility by administrators only (using request specs with RSpec and Capybara).

The idea is to have shared examples, which try to visit resource url as unauthenicated user, authenicated non-privileged user and administrator, and check if desired resource was loaded or not. I will show, how I used RSpec’s shared examples (with resource url as parameter) for this.

Create shared example group in request_shared_examples.rb file in spec/support directory:

shared_examples "accessible by admin" do |url|
  let(:url_path) { send(url) }

  context "unauthenticated" do
    it "redirects to root page with error" do
      visit url_path
      current_path.should == root_path
      page.should have_selector(".alert-error")
    end
  end

  context "for unauthorized user" do
    it "redirects to root page with error" do
      login_user(admin: false)
      visit url_path
      current_path.should == root_path
      page.should have_selector(".alert-error")
    end
  end

  context "for authorized user" do
    it "displays page without errors" do
      login_user(admin: true)
      visit url_path
      current_path.should == url_path
      page.should_not have_selector(".alert-error")
    end
  end
end

Include these examples in your specs with it_should_behave_like method:

describe "Items", :type => :request do
  it_should_behave_like("accessible by admin", :admin_items_path)
  # other examples
end

describe "Categories", :type => :request do
  it_should_behave_like("accessible by admin", :admin_categories_path)
  # other examples
end

NOTE: I’m passing url helpers’ names as symbols and evaluate them in shared example group (with let and send), because they can’t be evaluated in ExampleGroup context.