簡體   English   中英

如何使用 Minitest 測試 Pundit 政策?

[英]How to test Pundit policies with Minitest?

文件

gem 'pundit', '~> 0.2.1'

應用程序/控制器/application_controller.rb

class ApplicationController < ActionController::Base

  include Pundit
  ...

應用程序/政策/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

應用程序/政策/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

應用程序/控制器/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

測試/工廠/工廠.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

第一次嘗試

根據Minitest文檔,我嘗試了< Minitest::Test ,但得到gems/minitest-4.7.5/lib/minitest/unit.rb:19:in 'const_missing': uninitialized constant MiniTest::Test (NameError)這導致我發現master 中的文檔適用於 Minitest 5 所以我在版本 5 提交之前搜索了 Minitest 文檔,發現我們應該MiniTest::Unit::TestCase

測試/政策/book_policy_test.rb

require 'test_helper'
class BookPolicyTest < Minitest::Test
  ...
end

第二次嘗試(正確的超類)

測試/政策/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

重構一(創建'permit'方法)

測試/政策/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

重構二(創建“PolicyTest”類)

測試/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

測試/政策/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

重構三(創建 'forbid' 方法)

測試/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

測試/政策/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

我創建了一個 gem,用於使用名為policy-assertions 的minitest 測試專家。 這是您的測試的樣子。

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 現在提供Pundit#authorize ( https://github.com/elabs/pundit/pull/227 )。 所以 user664833 的 permit 方法可以更新如下:

def permit(current_context, record, action)
  Pundit.authorize(current_context, record, action)
rescue Pundit::NotAuthorizedError
  false
end

基於 user664833 的回答,我使用以下內容來測試 Pundit 的 policy_scope:

def permit_index(user, record)
  (record.class.to_s + 'Policy::Scope').constantize.new(user, record.class).resolve.include?(record)
end

例如:

assert permit_index(@book.user, @book)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM