Search code examples
ruby-on-railsrubyrspec-railscancancancancan

Testing CanCan ability to view pages with RSpec for controller without a model


I have a Rails app that has a controller without a model/object for handling reports:

# reports_controller.rb

class ReportsController < ApplicationController

  authorize_resource :class => false

  def index
  end

  def report_title
    # etc.
  end

  # etc.

end

User objects can have a role of either: admin, normal, or viewer. An admin can do anything, a normal user has a bunch of rules around it, and a viewer can cannot do anything with any of the app's objects, but they are allowed to view all the reports.

# ability.rb

def initialize(user)
  if user.admin?
    can :manage, :all

  elsif user.normal?
    # stuff according to business rules...

  elsif user.viewer?
    can [:index, :report_title, ...], :report
  end
end

This works as intended (as all of the other controller have load_and_authorize_resource), but how do I go about unit testing these lines in ability.rb? Can I do it in ability_spec.rb with my other unit tests, or do I have to test it only via requests?

By the way, testing via requests does work, I'm just wondering if it is possible to test here instead / as well.

# ability_spec.rb

RSpec.describe User do
  describe "abilities" do
    subject(:ability){ Ability.new(user) }

    CRUD = [:create, :read, :update, :destroy]
    ALL_MODELS = # a list of models

    # ...

    context "a report viewer" do
      let(:user){ FactoryGirl.create(:user, :viewer) }

      it 'cannot do anything with any objects' do
        ALL_MODELS.each do |object|
          CRUD.each do |action|
            should_not be_able_to(action, object)
          end
        end
      end

      it 'can access all the report actions' do
        # ???
      end
    end
  end
end

Solution

  • Yes, you should be able to just test that ability in ability_spec.rb. I think adding the following lines might work:

    it 'can access all the report actions' do
      should be_able_to :index, ReportsController
      should be_able_to :report_title, ReportsController
    end
    

    if you set authorize_resource :class => ReportsController instead of false and use can [:index, :report_title, ...], :ReportsController instead of the symbol :report.

    I haven't tried it, so let me know if it worked.