mocking CanCan authorization while testing control

2020-07-22 19:31发布

问题:

Here is the Controller I want to test:

class UsersController < ApplicationController
  load_and_authorize_resource

  def index
    @users = User.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @users }
    end
  end

  def show
    @user = User.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @user }
    end
  end

  #other actions here

end

As you can see I use CanCan method load_and_authorize_resource so I've written a ControllerHelper for RSpec:

# spec/support/controller_spec.rb
module ControllerHelper
  def should_authorize(action, subject)
    controller.should_receive(:authorize!).with(action, subject).and_return('passed!')
  end
end

Then I've written the UserController Spec:

# spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do

  describe "GET #index" do
    before do
      @user = mock_model(User)
      should_authorize(:index, @user)
      User.stub!(:find).and_return(@user)
    end

    context "if the user passes all the authorizations" do
      it "populates an array of users" do
        User.should_receive(:find).and_return(@user)
        get :index
      end
      it "renders the :index view" do
        get :index
        response.should render_template :index
      end
    end
  end
end

but I get this error

Failures:

1) UsersController GET #index if the user passes all the authorizations populates an array of users
       Failure/Error: get :index
         #<UsersController:0x000000037ee750> received :authorize! with unexpected arguments
            expected: (:index, #<User:0x1bf597c @name="User_1001">)
            got: (:index, User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, created_at: datetime, updated_at: datetime, phone: string, address: string, city: string, cap: string, partitaiva: string, ragionesociale: string))
         # ./spec/controllers/users_controller_spec.rb:16:in `block (4 levels) in <top (required)>'

2) UsersController GET #index if the user passes all the authorizations renders the :index view
       Failure/Error: get :index
         #<UsersController:0x00000003802f98> received :authorize! with unexpected arguments
            expected: (:index, #<User:0x1bb0048 @name="User_1002">)
            got: (:index, User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, created_at: datetime, updated_at: datetime, phone: string, address: string, city: string, cap: string, partitaiva: string, ragionesociale: string))
         # ./spec/controllers/users_controller_spec.rb:19:in `block (4 levels) in <top (required)>'

EDIT

now my spec is like that, and it works!

# spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do

  describe "GET #index" do
    before do
      @user = mock_model(User)
      should_authorize(:index, User)
      User.stub!(:all).and_return(@user)
    end

    context "if the user passes all the authorizations" do
      it "populates an array of users" do
        User.should_receive(:all).and_return(@user)
        get :index
      end
      it "renders the :index view" do
        get :index
        response.should render_template :index
      end
    end
  end
end

Thanks,

enrico

回答1:

When you're using load_and_authorize_resource in cancan, it only passes an instance of user to the authorize method when the action is update, show, edit or destroy(I think). When the action is index it passes the User class instead.

That's why your expectation inside should_authorize fails

You can see how cancan does the loading here:

https://github.com/ryanb/cancan/blob/master/lib/cancan/controller_resource.rb#L76