Gemfile
gem 'pundit', '~> 0.2.1'
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Pundit
  ...
app/policies/application_policy.rb
class ApplicationPolicy < Struct.new(:user, :record)
  def index?  ; false;                              end
  def show?   ; scope.where(id: record.id).exists?; end
  def create? ; false;                              end
  def new?    ; create?;                            end
  def update? ; false;                              end
  def edit?   ; update?;                            end
  def destroy?; false;                              end
  def scope
    Pundit.policy_scope!(user, record.class)
  end
end
app/policies/book_policy.rb
class BookPolicy < ApplicationPolicy
  def create?
    record.new_record?
  end
  def new?
    create?      end
  def show?
    record.published? || user == record.user || user.is?(:admin)
  end
end
app/controllers/books_controller.rb
class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!, except: [:show]
  after_action :verify_authorized, except: :index
  after_action :verify_policy_scoped, only: :index
  # GET /books/1
  def show
    authorize(@book)
  end
  # GET /books/new
  def new
    @book = Book.new
    authorize(@book)
  end
  # POST /books
  def create
    @book = current_user.books.build(book_params)
    authorize(@book)
    if @book.save
      redirect_to @book, notice: 'Your book was successfully created.'
    else
      render action: 'new'
    end
  end
private
    def set_book
      @book = Book.find(params[:id])
    end
    def book_params
      params.require(:book).permit(:title, :description)
    end
end
test/factories/factories.rb
FactoryGirl.define do
  factory :user do
    sequence(:email) { |n| "email#{n}@x.com" }
    password  '12345678'
    password_confirmation  '12345678'
  end
  factory :book do
    title  'xx'
    user
  end
end
As per the Minitest docs I tried < Minitest::Test, but got gems/minitest-4.7.5/lib/minitest/unit.rb:19:in 'const_missing': uninitialized constant MiniTest::Test (NameError) which led me to discover that the docs in master are for Minitest 5. So I hunted down the Minitest docs before the version 5 commit and found that we should be subclassing MiniTest::Unit::TestCase.
test/policies/book_policy_test.rb
require 'test_helper'
class BookPolicyTest < Minitest::Test
  ...
end
test/policies/book_policy_test.rb
require 'test_helper'
class BookPolicyTest < Minitest::Unit::TestCase
  def test_new
    user = FactoryGirl.create(:user)
    book_policy = BookPolicy.new(user, Book.new)
    assert book_policy.new?
  end
  def test_create
    book = FactoryGirl.create(:book)
    book_policy = BookPolicy.new(book.user, book)
    assert !book_policy.create?
  end
end
test/policies/book_policy_test.rb
require 'test_helper'
class BookPolicyTest < Minitest::Unit::TestCase
  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end
  def test_create
    book = FactoryGirl.create(:book)
    assert !permit(book.user, book, :create)
  end
private
    def permit(current_user, record, action)
      self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
    end
end
test/test_helper.rb
class PolicyTest < Minitest::Unit::TestCase
  def permit(current_user, record, action)
    self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
  end
end
test/policies/book_policy_test.rb
require 'test_helper'
class BookPolicyTest < PolicyTest
  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end
  def test_create
    book = FactoryGirl.create(:book)
    assert !permit(book.user, book, :create)
  end
end
test/test_helper.rb
class PolicyTest < Minitest::Unit::TestCase
  def permit(current_user, record, action)
    self.class.to_s.gsub(/Test/, '').constantize.new(current_user, record).public_send("#{action.to_s}?")
  end
  def forbid(current_user, record, action)
    !permit(current_user, record, action)
  end
end
test/policies/book_policy_test.rb
require 'test_helper'
class BookPolicyTest < PolicyTest
  def test_new
    user = FactoryGirl.create(:user)
    assert permit(user, Book.new, :new)
  end
  def test_create
    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :create)
  end
end
require 'test_helper'
class BookPolicyTest < PolicyTest
  def test_new
    assert permit(User.new, Book.new, :new)
    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :new)
  end
  def test_create
    assert permit(User.new, Book.new, :create)
    book = FactoryGirl.create(:book)
    assert forbid(book.user, book, :create)
  end
  def test_show
    # a stranger should be able to see a published book
    stranger = FactoryGirl.build(:user)
    book = FactoryGirl.create(:book, published: true)
    refute_equal stranger, book.user
    assert permit(stranger, book, :show)
    # but not if it's NOT published
    book.published = false
    assert forbid(stranger, book, :show)
    # but the book owner still should
    assert permit(book.user, book, :show)
    # and so should the admin
    admin = FactoryGirl.build(:admin)
    assert permit(admin, book, :show)
  end
end
I created a gem for testing pundit with minitest called policy-assertions. Here's what your test looks like.
class ArticlePolicyTest < PolicyAssertions::Test
  def test_index_and_show
    assert_permit nil, Article
  end
  def test_new_and_create
    assert_permit users(:staff), Article
  end
  def test_destroy
    refute_permit users(:regular), articles(:instructions)
  end
end
Pundit now provides Pundit#authorize(https://github.com/elabs/pundit/pull/227).
So user664833's permit method can be updated as below:
def permit(current_context, record, action)
  Pundit.authorize(current_context, record, action)
rescue Pundit::NotAuthorizedError
  false
end
Building on user664833's answer I use the following to test Pundit's policy_scope:
def permit_index(user, record)
  (record.class.to_s + 'Policy::Scope').constantize.new(user, record.class).resolve.include?(record)
end
For example:
assert permit_index(@book.user, @book)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With