テスト対象
該当するコントローラ | 該当するアクション |
---|---|
UsersController | edit, update |
環境
フォルダ、使用ファイル
種類 | ファイル名 |
---|---|
スペック | spec/features/users_edit_spec.rb |
スペック | 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 |
アウトラインの作成 1/2
ユーザ情報の変更(更新)
※ 未ログインの場合(ページ保護)のテストは
authorization_spec.rb
(別ファイル)にて作成
フィーチャスペック編 authorization_spec
users_edit_spec.rb
# spec/features/users_edit_spec.rb
RSpec.feature "UsersEdit", type: :feature do
# 未ログイン場合のスペックは、
# authorization_spec.rb にて(別ファイル)作成
describe "edit"
# 情報が valid の場合
context "valid info"
scenario "success edit profile"
# 情報が invalid の場合
context "invalid info"
scenario "fail edit profile"
end
スペック作成 1/2
users_edit_spec.rb
# spec/features/users_edit_spec.rb
require 'rails_helper'
RSpec.feature "UsersEdit", type: :feature do
include SupportModule
include_context "setup"
subject { page }
describe "edit" do
# 情報が valid の場合
context "valid info" do
scenario "success edit profile" do
login_as(user)
click_link "Settings"
should have_title("Edit user")
should have_css("h1", text: "Update your profile")
should have_link("change", href: "http://gravatar.com/emails")
expect {
fill_in_update_profile_form("New Name", "new@example.com")
click_button "Save changes"
}.to change(User, :count).by(0)
expect(user.reload.name).to eq "New Name"
expect(user.reload.email).to eq "new@example.com"
success_messages("Profile updated")
end
end
# 情報が invalid の場合
context "invalid info" do
scenario "fail edit profile" do
login_as(user)
click_link "Settings"
expect {
fill_in_update_profile_form("", "foo@")
click_button "Save changes"
}.to change(User, :count).by(0)
expect(user.reload.name).not_to eq ""
expect(user.reload.email).not_to eq "foo@"
error_messages("errors")
end
end
end
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
end
end
SupportModule の作成
↓の部分をメソッドとして定義
fill_in_update_profile_form
success_messages("Profile updated")
error_messages("errors")
support_module.rb
# spec/support/support_module.rb
module SupportModule
def fill_in_update_profile_form(name, email, password = "", confirmation = "")
fill_in "Name", with: name
fill_in "Email", with: email
fill_in "Password", with: password
fill_in "Confirmation", with: confirmation
end
def success_messages(msg)
expect(page).to have_css("div.alert.alert-success", text: msg)
end
def error_messages(msg = "")
if msg.empty?
should have_css('div.alert.alert-danger')
else
should have_css('div.alert.alert-danger', text: msg)
end
end
end
実行結果 1/2
$ bin/rspec spec/features/users_edit_spec.rb -e "info"
UsersEdit
edit
valid info
success edit profile
invalid info
fail edit profile
Finished in 4.96 seconds (files took 2.3 seconds to load)
2 examples, 0 failures
アウトラインの作成 2/2
users_edit_spec.rb
# spec/features/users_edit_spec.rb
RSpec.feature "UsersEdit", type: :feature do
# 未ログイン場合のスペックは、
# authorization_spec.rb にて(別ファイル)作成
describe "edit"
# (省略)
# admin属性
describe "admin-attribute"
# admin属性をweb経由で変更できないこと
scenario "not allow change via the web"
# フレンドリーフォワーディング
describe "friendly forwarding"
# ログイン後は、
context "after login (non-login-user)"
# 目的としていたページに遷移していること
scenario "render desired page"
end
スペック作成 2/2
-
patch user_path(user), params: { user: admin_params }
の部分は、直接HTTPリクエストを送るので、オプションtype: :request
をつけないとNoMethodError
になってしまう
users_edit_spec.rb
# spec/features/users_edit_spec.rb
RSpec.feature "UsersEdit", type: :feature do
describe "edit"
# (省略)
# admin属性
describe "admin-attribute", type: :request do
# web経由で変更できないこと
scenario "not allow change via the web" do
patch user_path(user), params: { user: admin_params }
expect(user.reload).not_to be_admin
should have_current_path("/")
end
end
# フレンドリーフォワーディング
describe "friendly forwarding" do
# ログイン後は、
context "after login (non-login-user)" do
# 目的としていたページに遷移していること
scenario "render desired page" do
visit edit_user_path(user)
error_messages("Please log in")
should have_current_path("/login")
login_as(user)
should have_current_path(edit_user_path(user))
should have_title("Edit user")
should have_css("h1", text: "Update your profile")
end
end
end
end
end
shared_context の作成
shared_context.rb
# spec/support/shared_context.rb
RSpec.shared_context "setup" do
# 属性をハッシュ化して呼ばれた時に使う
let(:admin_params) { attributes_for(:user, admin: true) }
end
ファクトリの使い方
FactoryBot.attributes_for(:user, admin: true)
- 属性値をハッシュ化する
$ rails console test --sandbox
[1] pry(main)>
[2] pry(main)> params = FactoryBot.attributes_for(:user, admin: true)
=> {:name=>"Example user",
:email=>"user@example.com",
:password=>"foobar",
:password_confirmation=>"foobar",
:admin=>true}
実行結果 2/2
$ bin/rspec spec/features/users_edit_spec.rb -e "admin-attribute"
UsersEdit
edit
admin-attribute
not allow change via the web
Finished in 1.58 seconds (files took 2.02 seconds to load)
1 example, 0 failures
$ bin/rspec spec/features/users_edit_spec.rb -e "friendly forwarding"
UsersEdit
edit
friendly forwarding
after login (non-login-user)
render desired page
Finished in 3.96 seconds (files took 2.09 seconds to load)
1 example, 0 failures
未ログインの場合(ページ保護)のスペック
- authorization_spec.rb (別ファイル)にて作成
アウトラインの作成
authorization_spec.rb
# spec/features/authorization_spec.rb
RSpec.feature "Authorization", type: :feature do
# ページ保護(アクセス権限)
describe "in UsersController"
describe "login is necessary"
# 未ログインの場合
context "when non-login"
describe "edit"
# ログインメッセージが出力されること
it "should have error_messages 'Please log in'"
# ログインページへリダイレクトされること
it "should have redirect to '/login'"
describe "update"
it "should have error_messages 'Please log in'"
it "should have redirect to '/login'"
end
shared_examples の作成
ログインメッセージ出力・ログインページへリダイレクトは、一般化して使い回せるようにしてみる
spec側でブロックをオブジェクト化(
Proc.new { }
)してsubject { }
に定義し、shared_examples側で、subject.call
して呼び出す
shared_expamples.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
スペック作成
authorization_spec.rb
# spec/features/authorization_spec.rb
RSpec.feature "Authorization", type: :feature do
include SupportModule
include_context "setup"
# アクセス権限
describe "in UsersController", type: :request do
# 未ログインの場合 (before_action のテスト)
describe "login is necessary" do
context "when non-login" do
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
end
end
end
end
実行結果
$ bin/rspec spec/features/authorization_spec.rb -e "edit" -e "update"
Authorization
in UsersController
login is necessary
when non-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"
Finished in 2.11 seconds (files took 2.38 seconds to load)
4 examples, 0 failures