sajad torkamani

Suppose you decide to implement authorization checks using a gem like Pundit. Pundit provides a nice DSL for writing policy specs, but it’s possible that even though you’ve written correct authorization policies, you forget to call them in your controller actions.

Pundit offers a built-in way of avoiding this mistake, but I personally find that method a little quirky and confusing. Instead, I prefer to include authorization checks as part of my feature tests. Something like this:

# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'Delete note', type: :feature do
  it "only the note's owner can delete the note", type: :request do
    jim, bob = create_pair(:user)
    bob_note = create(:note, user: bob)

    login_as(jim)

    expect { delete note_path(bob_note) }.to raise_unauthorized_error
  end
end

I like to use a raise_unauthorized_error custom RSpec matcher to improve code readability:

RSpec::Matchers.define :raise_unauthorized_error do
  match do |given_proc|
    begin
      given_proc.call
    rescue Pundit::NotAuthorizedError
      return true
    end

    false
  end

  supports_block_expectations

  failure_message do
    'expected Pundit::NotAuthorizedError but nothing was raised'
  end
end