Search code examples
ruby-on-railsrspeccancan

Mock the ability class


Here's my very simple ability class:

class Ability
    include CanCan::Ability

    def initialize(user)
        if user.has_role? :admin
            can :manage, :control_panel
        end
    end
end

How should I mock it in a controller spec?

Here's my control panel controller:

class Admin::ControlPanelController < ApplicationController
    authorize_resource class: false

    rescue_from CanCan::AccessDenied do |exception|
        redirect_to root_url, danger: "#{exception}"
    end

    def statistics
    end
end

Here's my control_panel controller spec:

describe '#statistics:' do
    let(:request){ get :statistics }

    context 'When guest;' do
        before do
            # HOW SHOULD I MOCK HERE?
        end 

        describe 'response' do
            subject { response }

            its(:status){ should eq 302 }
            its(:content_type){ should eq 'text/html' }
            it{ should redirect_to root_path }
        end

        describe 'flash' do
            specify { expect( flash[:danger] ).to eq "You do not have sufficient priviledges to access the admin area. Try logging in with an account that has admin priviledges." }
        end
    end

How should I mock the ability? Before, I was doing this:

let(:user){ FactoryGirl.create :user }
expect(controller).to receive(:current_user).and_return user
expect(user).to receive(:has_role?).with(:admin).and_return false

but that was before I was using cancan and was manually checking that the user had a certain role. This behaviour was happening in the application controller and so was very easy to mock. I'm having difficulty mocking this ability class :(

I want to mock it in different contexts. I'm feeling a bit lost because even if I do this:

expect(Ability).to receive(:asdasdadskjadawd?).at_least(:once)

No error is raised, though one is raised if I do spell 'Ability' wrongly so it's mocking the class ok...


Solution

  • I don't think you should be mocking the Ability class, especially not in a controller test. The Ability class is more like configuration than code; it doesn't change during your application. It's also an implementation detail that the controller shouldn't care about.

    Instead, you should be mocking your Users. It looks like you're using FactoryGirl; you could use FactoryGirl's traits to mock the various kinds of user you have:

    FactoryGirl.define do
      factory :user do
        name 'Bob'
        email '[email protected]
        role 'user'
    
        trait :admin do
          role 'admin'
        end
    
        trait :guest do
          role 'guest'
        end
      end
    end
    

    You can then use FactoryGirl.create :user if you need a regular user, and FactoryGirl.create :user, :admin if your test requires an admin.