Test Pundit’s headless policies with rspec

Saurabh Bhatia
Comix
Published in
2 min readDec 19, 2018

I recently started writing policy classes with Pundit for the comix web application. Dashboards controller is a classic case where I need to control the access of the page based on a user role.

My setup includes using devise for authentication, rolify gem for assigning roles to a user and pundit for policy management. Pretty standard so far. I however need to test my policy classes. I came across this article from Thunderbolt labs which talked about setting up pundit’s rspec helper with shoulda matchers. It seems like the cleanest way to test my policy classes. The syntax however requires a bit of an update since some of the methods they’re using in the helper class are deprecated. Here’s how my policy_helper.rb looks like :

RSpec::Matchers.define :permit do |action|
match do |policy|
policy.public_send("#{action}?")
end
failure_message do |policy|
"#{policy.class} does not permit #{action} on #{policy.record} for #{policy.user.inspect}."
end
failure_message_when_negated do |policy|
"#{policy.class} does not forbid #{action} on #{policy.record} for #{policy.user.inspect}."
end
end

As you can see failure_message_for_should and failure_message_for_should_not have since been deprecated. Now, my headless policy is a struct and assumes there is no model backing the controller.

class DashboardsPolicy < Struct.new(:user, :dashboards)
def index?
user.has_role? :creator
end
end

Setting up authorization in Dashboards controller is pretty straightforward too.

class DashboardsController < ApplicationController
before_action :authenticate_user!

def index
authorize :dashboards, :index?
end
end

Now, the spec should test whether the controller action is permitted using that policy or not for a particular user. In order to do that we’ll use the shoulda matchers we created earlier. We initialize a user object and pass it to the dashboard policy. Policy then validates the role and returns a boolean for us to work with.

require 'rails_helper'RSpec.describe DashboardsPolicy do
subject { DashboardsPolicy.new(user) }

context "for a buyer" do
let(:user) { create(:user) }
it { should_not permit(:index) }
end

context "for a creator" do
let(:user) { create(:user, :creator) }
it { should permit(:index) }
end
end

--

--

Saurabh Bhatia
Comix
Editor for

Programmer , Sci-fi writer, Cook, Open Source advocate.