質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.50%
RSpec

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

Ruby on Rails

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

トランザクション

トランザクションとは、関連・依存する処理を一連の不可分な処理単位として扱う処理方式を指します。トランザクションとして管理された処理は「すべて成功」か「すべて失敗」のいずれかであることが保証される。処理に失敗した場合は、一連の処理がロールバックされます。

Q&A

解決済

1回答

2568閲覧

Rspec トランザクション処理を失敗させたテストを書きたい

IRIESS

総合スコア45

RSpec

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

Ruby on Rails

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

トランザクション

トランザクションとは、関連・依存する処理を一連の不可分な処理単位として扱う処理方式を指します。トランザクションとして管理された処理は「すべて成功」か「すべて失敗」のいずれかであることが保証される。処理に失敗した場合は、一連の処理がロールバックされます。

0グッド

0クリップ

投稿2021/11/29 23:18

先輩エンジニアの皆様質問失礼致します。

updateアクションにリクエストが送られてきたら、agreement.stateと、room.consensusをupdate_attributeしています。この2つの一貫性を保つためにもトランザクション処理をしているのですが、何らかが原因でupdate_attributeが失敗したときのresponseをテストしたいです。

テスト内でどのようにすればトランザクションの中のupdate_attributeを失敗させればいいか分かりません。
ご教授お願い致します。

ruby

1#agreements_controller.rb 2 3def update 4 agreement = Agreement.find(params[:id]) 5 room = agreement.room 6 if (api_user_signed_in? && current_api_user = agreement.user) || (api_host_signed_in? && current_api_host = agreement.host) 7 if agreement.start_time > 24.hours.since 8##############トランザクション############## 9 begin 10 ActiveRecord::Base.transaction do 11 agreement.update_attribute(:state, "変更申請中") 12 room.update_attribute(:consensus, "交渉中") 13 end 14 obj = {agreement: agreement, room: room} 15 render json: obj, status: 200 16 rescue => error 17 render json: {error: error}, status: 400 18 end 19########################################### 20 else 21 render body: nil, status: 400 22 end 23 else 24 render body: nil, status: 403 25 end 26 end

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

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

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

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

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

guest

回答1

0

ベストアンサー

この場合、update_attributeで変更する値は、定数ですね。ならば、agreementのカラム値の組合わせによっては、その定数でupdateすることが失敗する可能性が存在しますか?
もし、存在するならば、テストにおいて、そのような組合わせのデータのagreementを作って、それに対してupdateを実行させれば、失敗させられます。
それが正攻法です。


もしそのような組合わせが存在せず、よほど奇妙な事態(たとえば、Databaseへの接続が物理的に突然絶たれるなど)でない限りは失敗するはずがない場合は、敢えてエラーを起こすのは容易ではないと思います。
力技でよければ、以下の方法が考えつきました。

端的には、test環境におけるある特定の条件において、陽に失敗させます。たとえば、begin文の後もしくはagreement.update_attributeの直前に、次の一行を加えます。

ruby

1raise 'my-error' if Rails.env.test? && 0 == params[:id].to_i

そして、テストの時、ID=0のデータをDBに作って、そのIDをテストの時に呼びます。そうすると、RuntimeErrorで失敗します。あるいは、IF文中でagreement = nilとすればNoMethodError例外によりupdate_attributeが失敗しますし、agreement.freezeにすればFrozenError 例外が出て失敗します。

なお、成功をテストする場合にはID=0 になることはあり得ない、ことを保証するよう、気をつけましょう(Rails用のDBのデフォルト設定では、IDは1から始まるのでこの条件は満たされているでしょう)。

現実には、このようにテストのそれも特定の条件の時だけ呼ばれる行を、実際の Controllerのコードの中に含ませることはあまりよいpracticeとは言えないかも知れませんが。
それを避けるためには、たとえばそのようなtestsを一度実行して期待通りの挙動であることを確認した後、ソースコードの中から該当行をコメントアウトする(必要ならばテストコードからもコメントアウトする)、ようにした方がいいかもしれませんか。

開発のcoding policy次第でしょう。

投稿2021/11/30 19:22

編集2021/11/30 20:13
MasaSakano

総合スコア188

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

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

IRIESS

2021/11/30 19:32

ご回答ありがとうございます。 勉強になりました。 そもそも失敗する可能性が考えにくい場合はテストする必要もないということですね。 ならばそもそもトランザクション処理にする必要もないのですかね。。。 力技で失敗させる方法も勉強になりました!ありがとうございました!
MasaSakano

2021/11/30 20:14

updateを二つ入れるならば、transactionで囲むのはとてもよい習慣だと思います。 特に、商用サーバーで一秒間に千回のアクセスがあり得る場合とかならば、必須かもしれませんね。 失敗の場合をちゃんとテストするのもとてもよい習慣だと思います。ただし、本番環境では全く使わないどころか邪魔なだけの文を残すかどうかは別問題ですか。 なお、テスト環境に限るという条件をつける常套手段を思い出したので、回答編集しておきました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問