I am using Pundit for authorization on my Rails app and I am unit testing my requests. I have already successfully tested the policy but now I want to verify that my request is using that policy. I want to do it in a generic way, that I can use in ANY request spec (regardless of the controller, the action, and the policy). To be honest at this point I would be conformed with a way of saying "expect any policy to receive authorize", generic to all requests.
For the index action (that uses the policy scope) it was easy:
In the request describe I say
RSpec.describe 'GET /companies', type: :request do
include_context 'invokes policy scope'
end
And then I defined the shared context as:
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
But how do I do the same for the authorize method. I don't know which concrete policy will be the one receiving it, neither which is the concrete controller that will invoke the authorize
.
I don't understand how pundit does not provide a custom matcher to verify a certain request is actually invoking a given policy, or to "simulate" authorized/unauthorized scenarios so I test that my request returns the correct status code.
Any ideas?
when you include Pundit
module in your controllers then the policy_scope
, and the autherize
methods become available inside your controller as public methods.
so when you send an get
or post
request to your controller rails behind the scenes creates an instance of the controller ControllerClass.new
so what you need is to mock the authorize
method on the instantiated controller object.
to mock the method on that object you need to know or have that object in your tests which is not possible. but hopefully you can mock a public method on any class instance in advance.
so to mock the authorize
method you will write :
expect_any_instance_of(described_class).to receive(:authorize).with(any_params_you_want).and_return(nil)
expect_any_instance_of
is a method provided by rspec to mock any instantiated object of a certain class. click here to learn more
so no need to reference Pundit
class in your tests anymore. actually that would create a dependency in our tests on the class name of the gem, which you do not need since you can test both methods as explained above.