1

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

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

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

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

我不太确定从这里去哪里。我最多删除 6 个模型,只是为了编写一个测试。我知道能力是有用的,这就是ability_spec.rb它的用途。

所以这个问题真的是2倍的:

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

Edit require 'spec_helper' include 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

谢谢,
罗比

4

5 回答 5

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)

于 2010-11-10T19:40:45.327 回答
2

我确实单独测试了 cancan 模型,但测试了它在什么条件下允许的内容。

我想如果你正在做类似的事情

authorize! :take_over, @the_world

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

您可以将Ability.can stub掉吗?类并让它响应真/假,并测试您的控制器在可以(更重要的是)无法继续时如何处理。

于 2010-10-28T21:09:11.693 回答
1

与 Sam 的回答类似,但来自 CanCan wiki 测试页面:

控制器测试

如果您想在控制器级别测试授权功能,一种选择是登录具有适当权限的用户。

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

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

于 2013-01-28T13:27:22.263 回答
1

我认为授权需要主要针对控制器进行,以确保您的授权与控制器正常工作。因此,要使其干燥,您可以实现自己的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

然后用你自己的方式实现这些匹配器来测试一个请求将如何被授权或不被授权

RSpec::Matchers.define :be_authorized do
  match do |block|
    block.call
    expect(response).to be_success
  end

  def supports_block_expectations?
    true
  end
end

RSpec::Matchers.define :be_un_authorized do
  match do |block|
    expect{
      block.call
    }.to raise_error(Pundit::NotAuthorizedError)
  end

  def supports_block_expectations?
    true
  end
end
于 2016-02-14T08:48:02.800 回答
0

你为什么不包括一个

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

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

one: 
  id: 1 
  username: my_username 
  is_ultrasuper: 1

然后在您的测试设置中登录该用户。这在测试中你应该可以做任何事情。

于 2011-10-16T03:50:54.187 回答