在测试控制器时保持干燥,通过CanCan授权

时间:2010-10-28 20:51:58

标签: ruby-on-rails testing cancan

我正在追溯使用RSpec为Rails项目编写一些测试。

我正在使用CanCan gem来提供授权。我决定编写一个测试ability.rb模型的规范。 然后我继续测试我剩下的模型。

我已经转向控制器,我遇到了一个巨大的障碍:我正在重新测试我的能力!

基本上,我必须剔除一系列模型,并剔除它们的关联;否则响应只返回403 Forbidden 原因是控制器主要负责担心授权。

我不太确定从哪里开始。我正在编写多达6个模型,只是为了编写一个测试。 我知道能力工作,这就是ability_spec.rb的用途。

所以这个问题实际上是双重的:

  1. 我应该单独测试能力模型吗?
  2. 控制器测试是否应该考虑适当的权限?

  3. 修改     需要'spec_helper'     包括Devise :: TestHelpers#以使您的规范可以访问帮助者

    describe TokensController do
      before(:each) do
        @mock_user = User.new(:username => "bob", :email => "user@user.com", :password => "longpassword")
        @mock_user.role = "admin"
        sign_in @mock_user
        #Ability.stub!('can').and_return(true)
      end
      it "should let me see grids/:g_id/tokens index" do
        test_grid = mock_model(Grid)
        test_token = mock_model(Token)
        Grid.stub!(:find).and_return(test_grid)
        Token.stub!(:find).and_return(test_token)
        get 'index'
    
        a1 = Ability.new(@mock_user)
        a1.can?(:index, Token).should be_true # This line works fine; as it should
        puts response.status #This returns 403, which means CanCan::AccessDenied was raised
      end
    end
    

    谢谢,
    罗比

5 个答案:

答案 0 :(得分:4)

不确定这对你来说是否为时已晚,但我遇到了同样的问题,并使用以下代码示例解决了这个问题 -

before do
  @user = Factory.create(:user)
    sign_in @user

    @abilities = Ability.new(@user)
    Ability.stub(:new).and_return(@abilities)
  end
end

我已经删除了Ability#new,给了我一个控制当前用户的Ability实例的引用。然后,我可以删除这样的具体能力:

@abilities.stub!(:can?).with(:destroy, regatta).and_return(true)

或授予管理员权限:

@abilities.stub!(:can?).and_return(false)

答案 1 :(得分:2)

我会分别测试cancan模型,但测试它在什么条件下允许的内容。

我想如果你正在做像

这样的事情
authorize! :take_over, @the_world

然后我认为你应该在控制器中测试它。我不确定你是否需要测试你的模型的所有6个版本。

你可以删除Ability.can吗? class并让它响应true / false,并测试控制器何时可以(更重要的是)在无法继续时处理。

答案 2 :(得分:1)

类似于Sam的回答,但是来自CanCan维基页面的测试:

控制器测试

如果要在控制器级别测试授权功能,一个选项是登录具有相应权限的用户。

user = User.create!(:admin => true) # I recommend a factory for this
# log in user however you like, alternatively stub `current_user` method
session[:user_id] = user.id 
get :index
assert_template :index # render the template since he should have access

或者,如果您想独立于Ability类内部测试控制器行为,则可以使用您想要的任何行为来简化该功能。

def setup
  @ability = Object.new
  @ability.extend(CanCan::Ability)
  @controller.stubs(:current_ability).returns(@ability)
end

test "render index if have read ability on project" do
  @ability.can :read, Project
  get :index
  assert_template :index
end

如果您拥有非常复杂的权限,则会导致许多分支可能性。如果这些都在控制器层中进行了测试,那么它可能导致缓慢而膨胀的测试。相反,我建议保持控制器授权测试,并通过单元测试在能力模型中更彻底地测试授权功能,如上图所示。

答案 3 :(得分:1)

我认为需要主要针对控制器进行授权,以确保您的授权与控制器一起正常运行。为了使其 DRY ,您可以实现自己的function (d) { d.method1("#input a") d.method2("test") method3(1,2) } 来像这样使用

matcher

然后以自己的方式实施这些匹配器,以测试请求的授权方式

let!(:user) {create :user}
before { login_user_request user}

it "grants admin access to show action" do
  expect{ get :show, {id: user.id} }.to be_authorized
end
it "denies user access to edit action" do
  expect{ get :edit, {id: user.id} }.to be_un_authorized
end

答案 4 :(得分:0)

为什么不包括

can :manage, :all do
  user.is_ultrasuper == 1
end

在你的能力中,然后在你的一个夹具用户中有一个is_ultrasuper参数:

one: 
  id: 1 
  username: my_username 
  is_ultrasuper: 1

然后在设置测试时记录此用户。在测试中,你应该能够做任何事情。