'How to test Pundit Scopes in Rspec?

I've got a pretty simple Pundit policy with a scope for different user roles. I can't figure out how to test it in Rspec. Specifically, I don't know how to tell the scope what user is logged in before accessing the scope.

Here is what I've tried:

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

When I run Rspec, I get:

NoMethodError: undefined method `sign_in' for #<RSpec::ExampleGroups::ReportPolicy::Scope:0x00007f93241c67b8>

I use sign_in extensively in controller tests, but I guess that doesn't apply in a Policy test.

The Pundit docs says only:

Pundit does not provide a DSL for testing scopes. Just test it like a regular Ruby class!

So...does anyone have an example of testing a Pundit scope for a specific user? How do I tell the scope what current_user is?


FWIW, here's the essence of my policy:

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

In my controller, I call it as follows. I've confirmed that this works correctly in the real world, with admins seeing all reports, and other users only seeing reports for their account:

reports = policy_scope(Report)


Solution 1:[1]

Replacing

let(:records) { policy_scope(Report) } 

...with this:

let(:records) { ReportPolicy::Scope.new(user, Report).resolve }

...allows specifying the user to the policy. No call to sign_in is required.

Here is the complete solution:

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1