Edited at

Punditのrspecをカイゼンしてみた

More than 3 years have passed since last update.

Railsの権限管理にPunditを使うようになったのですが、標準のmatcherだとオブジェクトの操作に対して挙動を記述していきます。そうすると、あるユーザがどのような権限をもっているかがわかりにくい気がしました。

まず、Punditの標準でついてくるmatcherだとREADMEにあるようにこのような記述をします。


spec/policies/post_spec.rb

describe PostPolicy do

subject { described_class }

permissions :update? do
it "denies access if post is published" do
expect(subject).not_to permit(User.new(:admin => false), Post.new(:published => true))
end

it "grants access if post is published and user is an admin" do
expect(subject).to permit(User.new(:admin => true), Post.new(:published => true))
end

it "grants access if post is unpublished" do
expect(subject).to permit(User.new(:admin => false), Post.new(:published => false))
end
end
end


アプリケーションによっては、ユーザの権限がどのような操作ができるのかという書き方をしたほうがわかりやすい場合があるのではないでしょうか。そのような場合は、ここで紹介しているやり方が参考になりました。

http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/

spec/support/pundit_mathcer.rbを作成します。


spec/support/pundit_mathcer.rb

RSpec::Matchers.define :authorize do |action|

match do |policy|
policy.public_send("#{action}?")
end

failure_message_for_should do |policy|
"#{policy.class} does not permit #{action} on #{policy.record} for #{policy.user.inspect}."
end

failure_message_for_should_not do |policy|
"#{policy.class} does not forbid #{action} on #{policy.record} for #{policy.user.inspect}."
end
end


spec/rails_helper.rbに下記を追加します。


spec/rails_helper.rb

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}


そうすると、このような記述でPolicyのspecが書けます。


spec/policies/article_spec.rb

require 'rails_helper'

describe ArticlePolicy do
subject { ArticlePolicy.new(user, article) }

let(:article) { FactoryGirl.create(:article) }

context "for a visitor" do
let(:user) { nil }

it { should authorize(:show) }

it { should_not authorize(:create) }
it { should_not authorize(:new) }
it { should_not authorize(:update) }
it { should_not authorize(:edit) }
it { should_not authorize(:destroy) }
end

context "for a user" do
let(:user) { FactoryGirl.create(:user) }

it { should authorize(:show) }
it { should authorize(:create) }
it { should authorize(:new) }
it { should authorize(:update) }
it { should authorize(:edit) }
it { should authorize(:destroy) }
end
end