[英]How to test Pundit Scopes in Rspec?
I've got a pretty simple Pundit policy with a scope for different user roles.我有一个非常简单的 Pundit 策略,其中包含不同用户角色的范围。 I can't figure out how to test it in Rspec.
我不知道如何在 Rspec 中测试它。 Specifically, I don't know how to tell the scope what user is logged in before accessing the scope.
具体来说,我不知道如何在访问范围之前告诉范围什么用户登录了。
Here is what I've tried:这是我尝试过的:
let(:records) { policy_scope(Report) }
context 'admin user' do
before(:each) { sign_in(admin_user) }
it { expect(reports.to_a).to match_array([account1_report, account2_report]) }
end
context 'client user' do
before(:each) { sign_in(account2_user) }
it { expect(reports.to_a).to match_array([account2_report]) }
end
When I run Rspec, I get:当我运行 Rspec 时,我得到:
NoMethodError: undefined method `sign_in' for #<RSpec::ExampleGroups::ReportPolicy::Scope:0x00007f93241c67b8>
I use sign_in
extensively in controller tests, but I guess that doesn't apply in a Policy test.我在控制器测试中广泛使用
sign_in
,但我想这不适用于策略测试。
The Pundit docs says only: Pundit 文档只说:
Pundit does not provide a DSL for testing scopes.
Pundit 不提供用于测试范围的 DSL。 Just test it like a regular Ruby class!
只需像普通的 Ruby 类一样测试它!
So...does anyone have an example of testing a Pundit scope for a specific user?那么...有没有人为特定用户测试 Pundit 范围的示例? How do I tell the scope what current_user is?
如何告诉范围 current_user 是什么?
FWIW, here's the essence of my policy: FWIW,这是我政策的精髓:
class ReportPolicy < ApplicationPolicy
def index?
true
end
class Scope < Scope
def resolve
if user.role == 'admin'
scope.all
else
scope.where(account_id: user.account_id)
end
end
end
end
In my controller, I call it as follows.在我的控制器中,我将其称为如下。 I've confirmed that this works correctly in the real world, with admins seeing all reports, and other users only seeing reports for their account:
我已经确认这在现实世界中可以正常工作,管理员可以看到所有报告,而其他用户只能看到他们帐户的报告:
reports = policy_scope(Report)
You can instantiate a policy scope with:您可以使用以下方法实例化策略范围:
Pundit.policy_scope!(user, Report)
Which is short for:简称:
ReportPolicy::Scope.new(user, Report).resolve
Note that you don't need to do any actual steps of signing the user in. user
is just an object that your policy scope takes as an initializer argument.请注意,您不需要执行任何使用户登录的实际步骤。
user
只是您的策略范围作为初始化参数的对象。 Pundit is after all just plain old OOP. Pundit 毕竟只是普通的旧 OOP。
class ApplicationPolicy
# ...
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end
As to the actual spec I would write it as:至于实际规格,我将其写为:
require 'rails_helper'
require 'pundit/rspec' # optional - but includes some nice matchers for policies
RSpec.describe ReportPolicy, type: :policy do
let(:user) { User.new }
let(:scope) { Pundit.policy_scope!(user, Report) }
# ... setup account1_report etc
describe "Scope" do
context 'client user' do
it 'allows a limited subset' do
expect(scope.to_a).to match_array([account2_report])
end
end
context 'admin user' do
let(:user) { User.new(role: 'admin') }
it 'allows access to all the reports' do
expect(scope.to_a).to match_array([account1_report, account2_report])
end
end
end
end
Avoid constructs such as it { expect ... }
and use it blocks that describe the actual behaviour that you are testing or you will end up with really cryptic failure messages and tests that are hard to understand.避免使用
it { expect ... }
类的结构,并使用 it 块来描述您正在测试的实际行为,否则您最终会得到非常神秘的失败消息和难以理解的测试。 The one-liner syntax it { is_expected.to ... }
should only be used to help avoid duplication in situations where the doc string and the matcher used in the example mirror each other exactly. 单行语法
it { is_expected.to ... }
应该只用于帮助避免在示例中使用的文档字符串和匹配器相互精确镜像的情况下出现重复。
Replacing更换
let(:records) { policy_scope(Report) }
...with this: ...有了这个:
let(:records) { ReportPolicy::Scope.new(user, Report).resolve }
...allows specifying the user to the policy. ...允许为策略指定用户。 No call to sign_in is required.
无需调用 sign_in。
Here is the complete solution:这是完整的解决方案:
let(:records) { ReportPolicy::Scope.new(user, Report).resolve }
context 'admin user' do
let(:user) { admin_user }
it { expect(reports.to_a).to match_array([account1_report, account2_report]) }
end
context 'client user' do
let(:user) { account2_user }
it { expect(reports.to_a).to match_array([account2_report]) }
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.