0

I am trying to test my cancancan abilities using rspec

but as opposed to testing for what a particular user can do, I am trying to test for what a user should not be able to do.

Now, I have a block of context like so:

context "for a manager" do
  before do
    @manager = FactoryGirl.build(:user, :manager)
    @ability = Ability.new(@manager)
  end

  it "should not be able to create Questions" do
    expect(@ability).not_to be_able_to(:create, Question.new)
  end

  it "should not be able to read Questions" do
    expect(@ability).not_to be_able_to(:read, Question.new)
  end

  it "should not be able to update Questions" do
    expect(@ability).not_to be_able_to(:update, Question.new)
  end

  it "should not be able to delete Questions" do
    expect(@ability).not_to be_able_to(:destroy, Question.new)
  end
end

This clearly shows that a user of type manager should not have any form of access to the Question model.

Is there a direct way to write this whole block in a single it block, with only one expect?

I have thought about writing it as follow:

context "for a manager" do
  before do
    @manager = FactoryGirl.build(:user, :manager)
    @ability = Ability.new(@manager)
  end

  it "should not be able to manage Questions" do
    expect(@ability).not_to be_able_to(:manage, Question.new)
  end
end

But I'm thinking that this may not necessarily do what I'm intending it to do, as this test will pass is as much as one of the ability for that resource is not granted.

So, in short, is there a direct way to test such scenarios? Thanks to all.

x6iae
  • 4,074
  • 3
  • 29
  • 50

1 Answers1

7

First of all, I advise you to use an explicit subject for @ability so you can use the one-liner syntax like in the example below.

describe Role do
  subject(:ability){ Ability.new(user) }
  let(:user){ FactoryGirl.build(:user, roles: [role]) }

  context "when is a manager" do
    let(:role){ FactoryGirl.build(:manager_role) }

    it{ is_expected.not_to be_able_to(:create, Question.new) }
    it{ is_expected.not_to be_able_to(:read, Question.new) }
    it{ is_expected.not_to be_able_to(:update, Question.new) }
    it{ is_expected.not_to be_able_to(:destroy, Question.new) }
  end
end

Updated after your comment

But you can also summarize all this 4 expectations to simply

%i[create read update destroy].each do |role|
  it{ is_expected.not_to be_able_to(role, Question.new) }
end
Arun Kumar Mohan
  • 11,517
  • 3
  • 23
  • 44
Nuno Costa
  • 1,210
  • 11
  • 12
  • Thanks, @Nuno Costa. However, I am looking for a way to test for what ability `cant do` as opposed to what ability `can` do. e.g: ` it{ is_expected.not_to be_able_to(:manage, Question.new) }` as opposed to: `it{ is_expected.to be_able_to(:manage, Question.new) }` – x6iae Sep 14 '15 at 16:31
  • I've updated my answer. Checking this 4 actions may not be enough, because you might have extra actions in your controller. See https://github.com/CanCanCommunity/cancancan/wiki/Action-Aliases – Nuno Costa Sep 15 '15 at 10:04
  • A bit late, but might be useful for future readers. It is advised to use `let` vs `before` blocks for setting variables/records as `let` creates it only once thus improving performance whereas `before` block creates new record for every test. – jedi May 24 '19 at 09:46