代码之家  ›  专栏  ›  技术社区  ›  David Hempy

如何在rspec中测试专家作用域?

  •  0
  • David Hempy  · 技术社区  · 5 年前

    我有一个非常简单的专家策略,它为不同的用户角色提供了一个作用域。我不知道如何在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不提供用于测试作用域的dsl。像普通的ruby类一样测试它!

    那么…有没有人有一个测试特定用户的权威范围的例子?如何告诉作用域当前用户是什么?


    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 回复  |  直到 5 年前
        1
  •  1
  •   max    5 年前

    可以使用以下命令实例化策略作用域:

    Pundit.policy_scope!(user, Report)
    

    简称:

    ReportPolicy::Scope.new(user, Report).resolve
    

    请注意,您不需要执行任何实际的用户登录步骤。 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
    

    避免诸如 it { expect ... } 并使用它来描述您正在测试的实际行为,否则您将得到非常神秘的失败消息和难以理解的测试。这个 one-liner syntax it { is_expected.to ... } 只应在示例中使用的文档字符串和匹配器彼此完全镜像的情况下使用,以帮助避免重复。

        2
  •  1
  •   David Hempy    5 年前

    替代

    let(:records) { policy_scope(Report) } 
    

    ……用这个:

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

    …允许指定策略的用户。不需要呼叫登录。

    以下是完整的解决方案:

    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