テスト対象
該当するコントローラ | 該当するアクション |
---|---|
UsersController | index, create, edit, update, destroy, following, followers |
MicropostsController | create, destroy |
環境
フォルダ、使用ファイル
種類 | ファイル名 |
---|---|
スペック | spec/features/authorization_spec.rb |
サポートモジュール | spec/support/support_module.rb |
shared_context | spec/support/shared_context.rb |
shared_examples | spec/support/shared_examples.rb |
ファクトリ(ユーザ) | spec/support/factories/users.rb |
ファクトリ(マイクロポスト) | spec/support/factories/microposts.rb |
未ログインの場合(ページ保護)のスペック
アウトラインの作成
authorization_spec.rb
# spec/features/authorization_spec.rb
RSpec.feature "Authorization", type: :feature do
# アクセス権限
describe "in UsersController"
# 未ログインの場合 (before_action のテスト)
describe "login is necessary"
context "when non-login"
describe "index"
# エラーメッセージ、ログインページにリダイレクト
describe "edit"
# エラーメッセージ、ログインページにリダイレクト
describe "update"
# エラーメッセージ、ログインページにリダイレクト
describe "destroy"
# エラーメッセージ、ログインページにリダイレクト
describe "following"
# エラーメッセージ、ログインページにリダイレクト
describe "followers"
# エラーメッセージ、ログインページにリダイレクト
# ユーザ削除権限
describe "authorization of delete user"
# adminユーザの場合
context "admin"
# ユーザの削除ができること
# 成功メッセージ
# 一般ユーザの場合
context "non-admin"
# ユーザの削除ができないこと
# ルートにリダイレクトされること
describe "in MicropostsController"
# マイクロポスト 投稿(作成)/削除
describe "authorization of create/destroy micropost"
context "when non-login"
describe "create"
# エラーメッセージ、ログインページにリダイレクト
describe "destroy"
# エラーメッセージ、ログインページにリダイレクト
end
shared_examples の作成 1/2
ログインメッセージ出力・ログインページへリダイレクトは、一般化して使い回せるようにしてみる
spec側でブロックをオブジェクト化(
Proc.new { }
)してsubject { }
に定義し、shared_examples側で、subject.call
して呼び出す
shared_examples.rb
# spec/support/shared_examples.rb
# 成功メッセージ
# flash[:success]
shared_examples_for "success message" do |msg|
it { subject.call; expect(flash[:success]).to eq msg }
end
# 失敗メッセージ
# flash[:danger]
shared_examples_for "error message" do |msg|
it { subject.call; expect(flash[:danger]).to eq msg }
end
# リダイレクト
# redirect to path
shared_examples_for "redirect to path" do |path|
it { subject.call; expect(response).to redirect_to path }
end
shared_examples の作成 2/2
↓のように、ユーザの削除(admin権限)成功/失敗のスペックを作成
it_behaves_like "success delete user"
it_behaves_like "fail delete user"
shared_examples.rb
# spec/support/shared_examples.rb
# ユーザの削除(admin権限)
# users#destroy
# 成功
shared_examples_for "success delete user" do
scenario "user decrememt -1" do
login_as(admin)
click_link "Users"
expect(page).to have_current_path("/users")
expect(page).to have_link('delete', href: user_path(User.first))
expect(page).to have_link('delete', href: user_path(User.second))
expect(page).not_to have_link('delete', href: user_path(admin))
expect {
click_link('delete', match: :first)
# 成功メッセージ
expect(page).to have_css("div.alert.alert-success", text: "User deleted")
}.to change(User, :count).by(-1)
end
end
# 失敗
shared_examples_for "fail delete user" do
scenario "user decrement 0" do
login_as(user)
click_link "Users"
expect(page).to have_current_path("/users")
expect(page).not_to have_link('delete', href: user_path(User.first))
expect(page).not_to have_link('delete', href: user_path(User.second))
expect {
# リンクが無いので、直接 HTTPリクエストを発行
delete user_path(User.first)
}.to change(User, :count).by(0)
end
end
スペック作成
authorization_spec.rb
# spec/features/authorization_spec.rb
require 'rails_helper'
RSpec.feature "Authorization", type: :feature do
include SupportModule
include_context "setup"
# アクセス権限
describe "in UsersController", type: :request do
describe "login is necessary" do
context "when non-login" do
describe "index" do
subject { Proc.new { get users_path } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "edit" do
subject { Proc.new { get edit_user_path(user) } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "update" do
subject { Proc.new { patch user_path(user), params: { user: update_params_1 } } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "destroy" do
subject { Proc.new { delete user_path(other_user) } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "following" do
subject { Proc.new { get following_user_path(user) } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "followers" do
subject { Proc.new { get followers_user_path(user) } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
end
end
# ユーザ削除権限
describe "authorization of delete user" do
before { users }
it { expect(User.count).to eq users.count }
# adminユーザの場合
context "admin" do
it_behaves_like "success delete user"
end
# 一般ユーザの場合
context "non-admin" do
it_behaves_like "fail delete user"
it { expect(page).to have_current_path("/") }
end
end
end
describe "in MicropostsController", type: :request do
# マイクロポスト 投稿(作成)/削除
describe "authorization of create/destroy micropost" do
context "when non-login" do
describe "create" do
subject { Proc.new { post microposts_path, params: post_params } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
describe "destroy" do
subject { Proc.new { delete micropost_path(my_post.id) } }
it_behaves_like "error flash", "Please log in"
it_behaves_like "redirect to path", "/login"
end
end
end
end
end
SupportModule, shared_context, ファクトリ
- SupportModule
support_module.rb
# spec/support/support_module.rb
module SupportModule
def login_as(user)
visit root_path
click_link "Log in"
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Log in"
end
end
- shared_context
shared_context.rb
# spec/support/shared_context.rb
RSpec.shared_context "setup" do
# ユーザ
# 遅延評価、呼ばれた時にDB保存される
let(:user) { create(:user) }
let(:admin) { create(:admin) }
# マイクロポスト
# 自分の投稿
let(:my_post) { create(:user_post) }
# 属性をハッシュ化して呼ばれた時に使う
let(:post_params) { attributes_for(:user_post) }
end
- ファクトリ
users.rb
# spec/factories/users.rb
FactoryBot.define do
# 自分
# factory [任意のファクトリ名], class: [クラス名]
factory :user, class: User do
name "Example user"
email "user@example.com"
password "foobar"
password_confirmation "foobar"
admin false
# 他人
factory :other_user do # => 入れ子にしているので[クラス名]は不要
name { Faker::Name.name }
email { Faker::Internet.email }
# 管理者ユーザ
factory :admin do
admin true
end
end
end
end
microposts.rb
# spec/factories/microposts.rb
FactoryBot.define do
# 自分のマイクロポスト
factory :user_post, class: Micropost do
content { Faker::Lorem.sentence(5) }
association :user, factory: :user
# 他人のマイクロポスト
factory :other_user_post do
content { Faker::Lorem.sentence(5) }
association :user, factory: :other_user
end
end
end
実行結果
$ bin/rspec spec/features/authorization_spec.rb
Running via Spring preloader in process 17859
Authorization
in UsersController
login is necessary
when non-login
index
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
edit
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
update
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
destroy
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
following
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
followers
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
authorization of delete user
should eq 30
admin
behaves like success delete user
user decrememt -1
non-admin
should have current path "/"
behaves like fail delete user
user decrement 0
in MicropostsController
authorization of create/destroy micropost
when non-login
create
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
destroy
behaves like error flash
should eq "Please log in"
behaves like redirect to path
should redirect to "/login"
Finished in 13.41 seconds (files took 2.92 seconds to load)
20 examples, 0 failures