如何在Rspec中测试Pundit示波器?

时间:2019-01-23 15:27:37

标签: ruby-on-rails ruby rspec pundit

我有一个非常简单的Pundit策略,其中包含针对不同用户角色的范围。我不知道如何在Rspec中对其进行测试。具体来说,在访问范围之前,我不知道如何告诉范围的用户。

这是我尝试过的:

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

运行Rspec时,我得到:

NoMethodError: undefined method `sign_in' for #<RSpec::ExampleGroups::ReportPolicy::Scope:0x00007f93241c67b8>

我在控制器测试中广泛使用sign_in,但我猜想这在策略测试中并不适用。

Pundit文档仅说:

  

Pundit不提供用于测试范围的DSL。就像常规的Ruby类一样测试它!

那么...有没有人举过为特定用户测试Pundit范围的示例?如何告诉范围current_user是什么?


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

在我的控制器中,我将其称为如下。我确认这在现实世界中是正确的,管理员可以看到所有报告,而其他用户只能看到其帐户的报告:

reports = policy_scope(Report)

2 个答案:

答案 0 :(得分:1)

替换

let(:records) { policy_scope(Report) } 

...与此:

let(:records) { ReportPolicy::Scope.new(user, Report).resolve }

...允许指定策略用户。无需致电sign_in。

这是完整的解决方案:

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

答案 1 :(得分:1)

您可以使用以下方法实例化策略范围:

Find in Folder...

以下简称:

Pundit.policy_scope!(user, Report)

请注意,您无需执行任何实际的登录用户步骤。ReportPolicy::Scope.new(user, Report).resolve 只是您的策略作用域用作初始化参数的对象。毕竟Pundit只是普通的OOP。

user

关于实际规格,我将其写为:

class ApplicationPolicy
  # ...
  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope.all
    end
  end
end

避免使用诸如require 'rails_helper' require 'pundit/rspec' 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 之类的结构,并使用它描述您要测试的实际行为的块,否则最终将获得难以理解的真正的失败故障消息和测试。 one-liner syntax it { expect ... }仅应用于在示例中使用的文档字符串和匹配器精确镜像的情况下避免重复。