Praveen R
Praveen R

Reputation: 200

how to write rspec for ability using cancancan

I have a implemented the post and user model where post model belongs to user model. I defined the ability model for authorisation so that only user who created the post can able to delete or update the post. i have the post controller like this:

def edit
  @post = @topic.posts.find(params[:id])
  authorize! :update, @post
end

Ability model:

class Ability
  include CanCan::Ability

  def initialize(user)
    can :update, Post do |p|
      p.user == user
    end

    can :destroy, Post do |p|
      p.user == user
    end

    can :destroy, Comment do |c|
      c.user == user
    end

    can :create, Post
    can :create, Comment
  end
end

What will be the rspec for the above model? Error:

expected #<User id: nil, email: "", created_at: nil, updated_at: nil> to respond to `able_to?`

  0) User Check for Abilities What User can do should be able to :create and #<Post id: nil, title: nil, body: nil, created_at: nil, updated_at: nil, topic_id: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, user_id: nil>
     Failure/Error: it { should be_able_to(:create, Post.new) }
       expected #<User id: nil, email: "", created_at: nil, updated_at: nil> to respond to `able_to?`
     # ./spec/models/ability_spec.rb:8:in `block (4 levels) in <top (required)>'

expected #<User id: nil, email: "", created_at: nil, updated_at: nil> to respond to `able_to?`

  0) User Check for Abilities What User can do should be able to :update and #<Post id: nil, title: nil, body: nil, created_at: nil, updated_at: nil, topic_id: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, user_id: nil>
     Failure/Error: it { should be_able_to(:update, Post.new) }
       expected #<User id: nil, email: "", created_at: nil, updated_at: nil> to respond to `able_to?`
     # ./spec/models/ability_spec.rb:9:in `block (4 levels) in <top (required)>'

Upvotes: 2

Views: 2695

Answers (2)

Based on the limited information you have provided, I'm going to share a sample spec which tests abilities.

describe "User" do
    describe "Check for Abilities" do
        let(:user) { FactoryGirl.create(:user) }

        describe "What User can do" do
            it { should be_able_to(:create, Post.new) }
            it { should be_able_to(:update, Post.new) }
        end
    end
end

What I have provided at the top was a Sample, using which you have to build upon it. My updated answer

require "cancan/matchers"

describe "User" do
  describe "abilities" do
    user = User.create!
    ability = Ability.new(user)
    expect(ability).to be_able_to(:create, Post.new)
    expect(ability).to_not be_able_to(:destroy, Post.new)
  end
end

Upvotes: 1

coorasse
coorasse

Reputation: 5528

Let's start by refactoring your ability file a little bit. Is always a good idea to use a hash of conditions, rather than blocks. Therefore your ability file should look like:

class Ability
  include CanCan::Ability

  def initialize(user)
    can [:update, :destroy], Post, user_id: user.id
    can :destroy, Comment, user_id: user.id
    can :create, Post
    can :create, Comment
  end
end

this is a shorter version of the one you have, and it does more.

Here is an example on how I would write the test, based on the cancancan documentation:

require 'cancan/matchers'
RSpec.describe Ability do  
  subject(:ability) { described_class.new(user) }

  context 'when there is no user' do
    let(:user){ nil }

    it { is_expected.to_not be_able_to(:create, Post)
    ....
  end

  context 'when the user is present' do
    let(:user){ User.new }

    it { is_expected.to be_able_to(:create, Post) }
    ...
end

end end

I let the rest of the tests to you.

Upvotes: 1

Related Questions