通过 CanCan 授权测试控制器时保持 DRY
-
27-09-2019 - |
题
我正在使用 RSpec 为 Rails 项目追溯编写一些测试。
我正在使用 CanCan gem 来提供授权。我决定编写一个规范来测试 ability.rb
模型。然后我继续测试剩余的模型。
我已经转向控制器,但遇到了一个巨大的障碍:我又在考验我的能力了!
基本上,我必须删除一系列模型,并删除它们的关联;否则响应只会返回 403 Forbidden
.
这样做的原因是控制器基本上负责担心授权。
我不太确定从这里该去哪里。我删除了多达 6 个模型,只是为了编写一个测试。我 知道 能力发挥作用,就是这样 ability_spec.rb
是为了.
所以这个问题实际上有两个方面:
- 我应该单独测试能力模型吗?
- 控制器测试是否应该关注适当的权限?
编辑需要'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
谢谢,
罗比
解决方案
我确实单独测试了康康模型,但测试了它在什么条件下允许什么。
我想如果你正在做类似的事情
authorize! :take_over, @the_world
那么我确实认为你应该在控制器中测试它。不过,我不确定您是否需要测试模型的所有 6 个版本。
你可以把Ability.can删除掉吗?类并让它响应真/假,并测试控制器在可以(更重要的是)无法继续时如何处理。
其他提示
不确定这对您来说是否为时已晚,但我刚刚遇到了同样的问题,并使用以下代码示例解决了它 -
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)
与 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
或者,如果您想独立于能力类内部的内容来测试控制器行为,则可以轻松地用您想要的任何行为来消除该能力。
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
如果您有非常复杂的权限,则可能会导致许多分支的可能性。如果这些都在控制器层进行测试,那么可能会导致测试缓慢且臃肿。相反,我建议保持控制器授权测试简单,并通过单元测试在能力模型中更彻底地测试授权功能,如顶部所示。
我认为授权主要是为了 控制器 确保您的授权与您的控制器正常工作。所以要做到这一点 干燥 你可以实现你自己的 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
你为什么不包括一个
can :manage, :all do
user.is_ultrasuper == 1
end
在你的能力中,然后在你的夹具用户之一中有一个 is_ultrasuper 参数:
one:
id: 1
username: my_username
is_ultrasuper: 1
然后在测试设置时登录该用户。在测试中你应该能够做任何事情。