テスト対象
該当するコントローラ | 該当するアクション |
---|---|
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