🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

2625閲覧

rspecで発生するlib/rack/session/abstract/id.rb:375:in 'commit_sesion'の解決方法について

Harluz

総合スコア19

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/12/28 12:59

編集2021/01/01 12:45

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

rspecのログが下の画像の通り
rspecのログ状況

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

indexアクションのログ
error logについて
error log

-----------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'

と記述されている。このコードは下記画像の通りとなる
commit_session
ログからもoptionsメソッドが未定義となっており、なんらかの形で定義できない現象が発生している。
このコードを遡り読み解いた限り(初学者なため間違えている可能性あり)
「requestを飛ばした際に、headerにHTTPリクエストと環境情報を渡している」
ことが分かった。
rspecのログではpatchリクエストは正常に動作していることから、環境情報に問題があるのではないか?と仮説を立てる。
とても初歩的で恥ずかしいが、rspecはどの環境で動作しているのか把握しておらず、検索したところ
「環境情報を指定しない限りはtest環境で動作する」
となっていた。そもそもtest用のDBやmigrateを行った記憶がなかった。
実際に実行してみるとエラーとなり、test環境のDBがcreateできない状態となっておりました。(docker環境)
今までrspecがdevelopment環境で動作していた(と思われる?)こととなりoptionsメソッドに悪影響を及ぼしていた可能性があります。
なんとか、テスト用のDBの作成migrateを実行し、再度テストを実行した結果、無事通りました。

neko_daisukeさんよりたくさんのヒントを頂き解決することができました。
本当にありがとうございます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

neko_daisuki

2020/12/29 06:16

いくつか質問させてください。 after_action は存在しますか? UsersController は ApplicationController を継承していますか? 他のサインインが必要なテストでも同様のエラーが出ますか? 画像の謎の処理のログはこれですべてですか?(見切れてる部分がないか)
Harluz

2020/12/29 06:36 編集

①after_actionは有りません。 ②class UsersController < ApplicationControllerとなっており継承されています。 ③indexやeditアクションにサインインを必要としていますが、問題なくテストは通ります。deleteアクションも必要ですが、まだテストコードを書いておりません。一旦作成してみましたら、追記いたします。 ④全てのログが写った画像を貼り付けます。
neko_daisuki

2020/12/29 08:15

rails_helper.rb の 78行目は DatabaseCleaner とかでしょうか? 謎の処理はデータベースのリセットで、データに変更があると失敗する? def update の if @user.update(user_params) を if true に変更してデータを更新しないようにして テストを走らせるとどうなりますか?
Harluz

2020/12/29 09:16

①「rails_helper.rb の 78行目は DatabaseCleaner とかでしょうか?」 config.after(:each) do DatabaseCleaner.clean ←78行目 end となっております。 ②if true に変更して実行しましたが、結果は変わりませんでした。 DatabaseCleanerの挙動等について調べてみます。
Harluz

2020/12/29 14:39

destroyアクションについてもテストコードを記述しましたが、pudateと同じ現象が発生します。 また、DatabaseCleanerについて調べましたが、少なくともexample毎にcleanしているためテスト自体には影響は及んでいないと考えております。
neko_daisuki

2020/12/30 06:53

リダイレクトの後にはじまる処理というのが他に思い当たりません。 他に出来そうなことは、 テスト失敗後に $ rails c -e test してデータが残っていない(削除できているか)か確認する。 セッション関連の記述をコメントアウト(before_action をはずして更新するユーザーを固定)し、 テストが通るか確認などでしょうか・・・
Harluz

2020/12/30 07:52 編集

before_actionを外して ①test.helperで定義したsign_inメソッドを使用する場合 ②何もログイン状態を再現しない場合(before_actionを外したことから不要になるため) の二つのパターンで実行してみました。 結果、 ①では今までと同じ挙動となり失敗 ②テスト成功(before_actionを戻すと期待しない動作となり失敗する) このことから、updateすること自体には問題はない模様です。 しかし、sign_inメソッドで呼び出しているActionDispatch::requestが問題になっていると予想されます。 因みに、neko_daisukiさんはrspec等でログイン状態の保持を再現する場合は、どのように実装しているのでしょうか?(前のトピックで教えていただいた通りなのか) お手数をおかけしますが、普段どのようにしているのか教えていただければ幸いです。(面倒な場合は、無理せずスルーして頂ければと思います)
neko_daisuki

2020/12/30 10:54

いま管理しているプロジェクトでは以前の回答の通りallow_any_instance_ofでテストしています。 patch も問題ないです。deviseは使わず、omniauthだけ使っています。
Harluz

2020/12/30 12:28

了解しました。 今日もう少し頑張ってダメだったら、一旦pendingにしてみようと思います。
Harluz

2020/12/30 14:17

dockerにて開発環境を構築しているのですが、もしかしたらDBがうまくcreateされていない可能性が出てきました。その修正(時間がかかりそうな雰囲気)をしてそれでも変化なしだった場合、諦めようと思います。
guest

回答1

0

自己解決

自己解決しましたので、本文に追加しました。
今回の問題点は
①rspecの実行環境を把握していなかったこと
②それに伴い必要なDBの作成やmigrateを行っていなかった
の初歩的な問題でした。

本文の方に追記しましたので、そちらを参照してください。

投稿2021/01/01 12:48

Harluz

総合スコア19

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問