gpt4 book ai didi

ruby-on-rails - 有没有办法在请求规范中模拟 Pundit 政策授权?

转载 作者:行者123 更新时间:2023-12-03 23:09:16 26 4
gpt4 key购买 nike

我在我的 Rails 应用程序上使用 Pundit 进行授权,我正在对我的请求进行单元测试。
我已经成功测试了该策略,但现在我想验证我的请求是否使用了该策略。
我想以一种通用的方式来做,我可以在任何请求规范中使用它(无论 Controller 、操作和策略如何)。
老实说,在这一点上,我会同意一种说法,即“期望获得授权的任何策略”,适用于所有请求。

对于索引操作(使用策略范围)很容易:

在请求中描述我说

RSpec.describe 'GET /companies', type: :request do
include_context 'invokes policy scope'
end

然后我将共享上下文定义为:
RSpec.shared_context 'invokes policy scope', shared_context: :metadata do
before do
expect(Pundit).to receive(:policy_scope!) do |user_context, relation|
expect(user_context.user).to eq(user)
expect(user_context.current_group).to eq(group)

relation
end
end
end

但是我如何对授权方法做同样的事情。我不知道接收它的具体策略是哪个,也不知道调用 authorize 的具体 Controller 是哪个。 .

我不明白权威如何不提供自定义匹配器来验证某个请求实际上是在调用给定策略,或者“模拟”授权/未授权场景,因此我测试我的请求是否返回正确的状态代码。

有任何想法吗?

最佳答案

所以,我有一些评论......

期望( expect 语句)应该在示例( it )块内,而不是在 before 内堵塞。 before 中的那种东西块是允许语句(例如, allow(ClassX).to receive(:method) { object } )、无法在测试变量声明中完成的数据修改或请求触发器。见 http://www.betterspecs.org/对于一些例子。 TL;DR,共享上下文不是一种适当的测试方式。

测试使用特定参数调用 policy_scope 的方法是:

# You can put something generic like this in a shared context and then
# define 'params' and 'scoped_result' as let vars in the specs that include
# the shared context
let(:request) { get '/companies' }
let(:params) { user_context or whatever }
let(:scoped_result) { relation }
# By using abstract variable names here, we make this reusable

it 'calls policy scope' do
expect(Pundit).to receive(:policy_scope!).with(params)
request
end

it 'scopes result' do
expect(Pundit.policy_scope!(params)).to eq(scoped_result)
end

要模拟它并 stub 其响应,您可以执行以下操作:

before do
# This ensures Pundit.policy_scope!(context) always returns scoped_result
allow(Pundit).to receive(:policy_scope!).with(context) { scoped_result }
end

...但这些是非常糟糕/脆弱的测试,特别是因为它与请求规范有关。您的 Pundit 策略应该已经在策略规范文件中进行了测试(请参阅 https://github.com/varvet/pundit#rspec ),因此 您真正想要做的是测试您的端点是否在给定某个输入(经过身份验证的策略管理对象)的情况下返回正确的输出(范围响应) .通过模拟响应来尝试覆盖 Pundit 的功能是一个坏主意,因为如果您对策略代码进行重大更改,您的端点的规范将继续通过。您在这里要做的是设置测试变量以适应会导致成功请求的情况,但要确保所有内容都是通用的,以便可以重用。对于请求规范,您可以执行以下操作:

# Shared context stuff
let(:json) { JSON.parse(response.body) }
let(:headers) { ...define the headers to use across requests...}

before { request }

shared_examples_for 'success' do
it { expect(response).to have_http_status(:success) }
it { expect(json).to eq(expected) } # or something
end


# User spec that includes shared context
include_context 'above stuff'

let(:request) { get '/companies', params: params, headers: headers }
let(:params) { { user_id: user.id } } # or something
let!(:admin_thing) {
...something that should be excluded by the pundit policy used by endpoint...
}

context 'restricted' do
let!(:user) { create :user, :restricted }
let(:expected) { ...stuff scoped to restricted user... }

it_behaves_like 'success'
end

context 'manager' do
let!(:user) { create :user, :manager }
let(:expected) { ...stuff scoped to manager user... }

it_behaves_like 'success'
end

context 'superuser' do
let!(:user) { create :user, :superuser }
let(expected) { ...unscoped stuff visible to super user... }

it_behaves_like 'success'
end

请注意,在更高级别(共享上下文),名称和函数是通用的。在较低级别(声明许可用户的规范),规范将抽象名称转换为特定于被测试端点的值。该规范还创建了一个不应由策略范围返回的附加对象(本质上是通过确认该对象已从结果中排除来测试范围)。希望有帮助。

关于ruby-on-rails - 有没有办法在请求规范中模拟 Pundit 政策授权?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59758888/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com