Rails Pundit 策略规范测试因 NoMethodError 而失败

Rails Pundit Policy Spec test failing with NoMethodError

我刚刚开始在我当前的项目中使用 Pundit 以及专家匹配器进行授权 gem。

到目前为止,它似乎对我来说通常是有效的,但我在测试中遇到了问题。

我通常尝试遵循 pundit-matchers readme and the Thunderbolt labs blog (http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) 中的示例。

这是我的策略文件;

#app/policies/procedure_policy.rb
class   ProcedurePolicy
    attr_reader :user, :procedure

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

    def index?
        user.admin?
    end

end

这是我的 policy_spec 文件

require 'rails_helper'

describe ProcedurePolicy do
    subject {described_class.new(user, procedure)}

    let(:procedure) {FactoryGirl.create(:procedure)}

    context "for a guest" do
        let(:user) {nil}
        it {is_expected.not_to permit_action(:index)}
    end

    context "for a non-admin user" do
        let(:user) {FactoryGirl.create(:user)}
        it {is_expected.not_to permit_action(:index)}
    end

    context "for an admin user" do
        let(:user) {FactoryGirl.create(:admin_user)}
        it {is_expected.to permit_action(:index)}
    end

end

我的 3 个测试中有 2 个通过; "for a non-admin user""for an admin user" 两个。 "for a guest" 测试失败

NoMethodError:
       undefined method `admin?' for nil:NilClass

现在我明白为什么了。我将 nil 传递给我的 ProcedurePolicy class 的 #index? 方法,它没有 #admin? 方法。但是我在网上找到的所有示例规格都是这样做的。我没看到什么。

如果我遗漏了一些非常明显的东西,我深表歉意。我已经离开编码几年了。

Pundit 调用 current_user 方法来设置 user,这通常由 Devise 等身份验证系统或自定义解决方案提供。这意味着在大多数情况下,期望你在达到 Pundit 逻辑之前总是有一层身份验证,所以当它到达那里时你永远不会将 user 设置为 nil。

如果您希望 Pundit 授权无需身份验证即可直接工作,则必须在策略定义中进行处理,例如:

class ProcedurePolicy    
  def index?
    user.present? && user.admin?
  end
end

http://www.rubydoc.info/gems/pundit#Policies

"being a visitor" 上下文用于在系统中当前没有经过身份验证的用户时测试授权尝试。由于没有用户登录,因此不存在用户 record/object - 缺少 用户对象是 nil。这就是为什么 Thunderbolt Labs 和 pundit-matchers 示例都使用 nil 来表示访问者。 nil 对象没有 admin? 方法,导致错误。

要正确处理策略中的 nil/guest 用户,请在检查用户是否为管理员之前检查用户对象是否存在,方法是直接检查用户对象或使用 present? 方法Rails,例如

def index?
  user.present? && user.admin?
end

或者只是:

def index?
  user && user.admin?
end

当没有用户登录时,您会发现 current_user 方法中的 Devise return nil 等身份验证系统。