railsにてユーザーの更新機能(updateアクション)を実装(rails tutorial参考)しました。
ユーザーの更新ができるのはログイン済みかつ自分のアカウントのみとなるように
before_actionを実行し再現しています。
このことからrspecのrequest specで
「ログイン済み」かつ「自分のアカウント」であるのであれば更新できるというテストを実行したいと思っております。
userscontroller
1before_action :logged_in_user, only: %i[index edit update destroy] 2before_action :correct_user, only: %i[edit update] 3 4def update 5 @user = User.find(params[:id]) 6 if @user.update(user_params) 7 flash[:success] = 'Profile updated' 8 redirect_to @user 9 else 10 render 'edit' 11 end 12 end 13 14 # ログイン済みユーザーかどうか確認 15 def logged_in_user 16 unless logged_in? ← sessionにuser_idがあるか 17 store_location ← ログイン前にgetしたURLを記憶する 18 flash[:danger] = 'Please log in.' 19 redirect_to login_url 20 end 21 end 22 23 # 正しいユーザーかどうか確認 24 def correct_user 25 @user = User.find(params[:id]) 26 redirect_to(root_url) unless current_user?(@user) 27 end
UsersRequestsSpec
1describe 'Check the update operation' do 2 let(:user) { FactoryBot.create(:user) } 3 # updateが成功するとき 4 context 'when editting is successful' do 5 it 'redirect to user_path(user)' do 6 sign_in(user) ←ログイン済みを再現 7 patch user_path(user), params: { user: { ← ログインしたuserでpathを流して、各項目を更新 8 name: 'Foo Bar', 9 email: 'foo@bar.com', 10 password: '', ←パスワードは記述しなくても問題ない仕様となっている。(元々の値が入るだけ) 11 password_confirmation: '' 12 } } 13 expect(response).to redirect_to user_path(user) 14 end 15 end 16end
sessionのuser_idを取得するためのメソッド(ログイン済み判定はsessionにuser_idがあるか、ないかとしているため)
testhelper
1def sign_in(user) 2 allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(user_id: user.id) 3end
rspeclog
1Failures: 2 3 1) UsersRequests PUT #update Check the update operation when editting is successful redirect to user_path(user) 4 Failure/Error: expect(response).to redirect_to user_path(user) 5 Expected response to be a <3XX: redirect>, but was a <500: Internal Server Error> 6 # ./spec/requests/users_requests_spec.rb:271:in `block (5 levels) in <top (required)>'
画像にも書き込んでおりますが、途中まで正常に動作しており、問題ないはずなのですが(ブラウザ上でも確認済み)なぜか別の処理が始まっています。
また、<500: Internal Server Error>も発生しております。
おそらくですが、test_helperに記載したsign_inメソッドが原因な感じがします。
「allow_any_instance_ofは非推奨」という記事を拝見しました。
このことから、allow_any_instance_ofを使用しない書き方に変更しようとしましたが、うまく動作しませんでした。(書き方が間違えているだけなのは把握しておりますが、正解がわからない状態です)
どんな些細なことでも構いませんので、どなたか教えていただきたいです。
下記は時系列に対応した状況を示したもの
----------------試したこと------------------------
作成したmock_sign_inメソッドについて
testhelper
1def mock_sign_in(user) 2 mock_ActionDispatch = instance_double(ActionDispatch::Request) 3 allow(mock_ActionDispatch).to receive(:session) 4 5 allow(mock_ActionDispatch).to receive(:session).and_return(user_id: user.id) 6end
様々なサイト等を参考に落とし込んでみたのですが、、、
多分、間違えてます。
-----------neko_daisukiさんより頂いた回答について-----------------
indexアクションのテストについて
usresrequestsspec
1describe 'GET #index' do 2 subject { response } 3 # ユーザーのログインの有無を確認 4 describe 'before_action :logged_in_user' do 5 let!(:user) { FactoryBot.create(:user) } 6 # ユーザーがログインしている場合 7 context 'The user is logged in' do 8 before do 9 sign_in(user) 10 get users_path 11 end 12 it 'responds successfully' do 13 is_expected.to be_successful 14 end 15 16 it 'returns a 200 response' do 17 is_expected.to have_http_status(200) 18 end 19 end 20 # ユーザーがログインしていない場合 21 context 'The user is not logged in' do 22 before do 23 get users_path 24 end 25 26 it 'redirect to login url' do 27 is_expected.to redirect_to login_url 28 end 29 30 it 'returns a 302 response' do 31 is_expected.to have_http_status(302) 32 end 33 end 34 end 35end
-----------RspecのデフォルトのDatabaseCleanerに戻した結果-----------------
railshelper
1config.use_transactional_fixtures = true
結果、ログの内容は変わリませんでした。
-----------CSRF対策を疑ってみた(空振りに終わる)-----------------
testrb
1config.action_controller.allow_forgery_protection = false
rails生成時にtest.rbには上記が記述されており、デフォルトではテスト環境でCSRF対策の検証は行わないことになっている。
以前、createアクションのテスト実行時にCSRF関係のエラーが発生した経緯が有りました。
今回においても、getリクエストのテスト(indexやedit)については通るが、update/deleteなどget以外のリクエストによりエラーが発生しているのではと仮説を立てました。
このことから、下記の通りにusers_controller.rbを修正してみました。
userscontrollerrb
1protect_from_forgery with: :exception, only: %i[create update destroy]
結果、何も変わりませんでした。
これも何かの参考になればと追記いたしました。
-----------自己解決について-----------------
結論:実行環境が異なっていたこと
rspecのログ画像を確認して頂きたいが、
log
1lib/rack/session/abstract/id.rb:375:in 'commit_sesion'
と記述されている。このコードは下記画像の通りとなる
ログからもoptionsメソッドが未定義となっており、なんらかの形で定義できない現象が発生している。
このコードを遡り読み解いた限り(初学者なため間違えている可能性あり)
「requestを飛ばした際に、headerにHTTPリクエストと環境情報を渡している」
ことが分かった。
rspecのログではpatchリクエストは正常に動作していることから、環境情報に問題があるのではないか?と仮説を立てる。
とても初歩的で恥ずかしいが、rspecはどの環境で動作しているのか把握しておらず、検索したところ
「環境情報を指定しない限りはtest環境で動作する」
となっていた。そもそもtest用のDBやmigrateを行った記憶がなかった。
実際に実行してみるとエラーとなり、test環境のDBがcreateできない状態となっておりました。(docker環境)
今までrspecがdevelopment環境で動作していた(と思われる?)こととなりoptionsメソッドに悪影響を及ぼしていた可能性があります。
なんとか、テスト用のDBの作成migrateを実行し、再度テストを実行した結果、無事通りました。
neko_daisukeさんよりたくさんのヒントを頂き解決することができました。
本当にありがとうございます。
回答1件
あなたの回答
tips
プレビュー