簡體   English   中英

如何在 Rspec 中測試 Pundit 范圍?

[英]How to test Pundit Scopes in Rspec?

我有一個非常簡單的 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)

您可以使用以下方法實例化策略范圍:

Pundit.policy_scope!(user, Report)

簡稱:

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

請注意,您不需要執行任何使用戶登錄的實際步驟。 user只是您的策略范圍作為初始化參數的對象。 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

至於實際規格,我將其寫為:

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

避免使用it { expect ... }類的結構,並使用 it 塊來描述您正在測試的實際行為,否則您最終會得到非常神秘的失敗消息和難以理解的測試。 單行語法it { is_expected.to ... }應該只用於幫助避免在示例中使用的文檔字符串和匹配器相互精確鏡像的情況下出現重復。

更換

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM