varvet / pundit

Minimal authorization through OO design and pure Ruby classes
MIT License
8.28k stars 630 forks source link

testing without 'pundit/rspec' #657

Closed engineer-plus-plus closed 4 years ago

engineer-plus-plus commented 4 years ago

I want to test the policies using rspec but without 'pundit/rspec'.  What's the right way to do it?  When I just code it like I think it should work, I get the "can't find current_user method" error.  What's a good way to stub it such that it's visible in the Pundit's scope?  I read a bunch of stuff on stackoverflow but couldn't find the answer.

I don't like using 'pundit/rspec' for a number of reasons.  First and foremost, tests that don't include the actual code used in the prod are hard to trust.  Part of the test is to make sure the tested functionality is invoked correctly. Having DSL do it under the hood denies that.  Second, I just want to use standard rspec syntax that everybody knows. Third, since 'pundit/rspec' uses obscure DSL, mhy IDE doesn't understand it.  So I can't run tests from IDE.

But I digress.  Can I please just get advice how to stub Devise's current_user?

Linuus commented 4 years ago

Can you show what you tried?

current_user is coming from the controller layer so it’s not involved pure in policy testing. If it’s looking for current_user you must be calling it.

pundit/rspec only adds some rspec matchers/a mini DSL. It has nothing to do with production code...only tests. Just like your own test code :) It doesn’t inject stuff all over the place.

Anyway... policies are just ruby objects. Instantiate and call as you wish.

YourPolicy.new(user, resource).update?

engineer-plus-plus commented 4 years ago

current_user

describe JobPolicy do
  before :all do
    @job_owner = User.create(role:'guest')
    @job = Job.create(owner: @job_owner)
    @admin = User.create(role:'admin')
  end
  it "allows admin to update job even though he is not the owner" do
    expect(policy(@job).update?).to be true 
  end
end

and the output is

     NameError:
       undefined local variable or method `current_user' for #<RSpec::ExampleGroups::JobPolicy:0x00007fd7fa69d000>
     # /Users/makedon/.rvm/gems/ruby-2.6.5/gems/pundit-2.1.0/lib/pundit.rb:317:in `pundit_user'
     # /Users/makedon/.rvm/gems/ruby-2.6.5/gems/pundit-2.1.0/lib/pundit.rb:261:in `policy'
     # ./spec/policies/job_policy_spec.rb:16:in `block (2 levels) in <top (required)>'

pundit/rspec

What I was saying is that in the actual code that runs in prod, I invoke the policy like this: policy(@job).update?. So I prefer to have exactly this in the tests.

YourPolicy.new(user, resource).update?

That's not a bad idea. I may just do this.