背景
RSpecで一度だけエラーを発生させ、
二度目にはオリジナルの処理を実行させたい時のテストの書き方を考えた。
そして一応思いついたのでメモ。
環境
ruby 2.5.1
Rails 5.2.2.1
rspec 3.8.0
結論から言うと
定義の中で再定義
# user.save!を失敗させる
allow(user).to receive(:save!) do
allow(user).to receive(:save!).and_call_original
raise.new(ActiveRecord::RecordNotSaved, "保存失敗")
end
補足
よくあるテスト内で特定のインスタンスの特定の関数でエラー投げたい、
と言うのはこんな感じでスタブ化できます。
# user.save!を失敗させる
allow(user).to receive(:save!).and_raise(ActiveRecord::RecordNotSaved, "error")
この手の、同一テスト中に、ある関数を最初の一回だけ失敗にして、
後は成功させたいなあ、って思うことがありました。
(多分こんなパターンは稀ですが)
こちらの記事のやつでいけるやん、って思ったのですが、
これだと二回目以降にstubがnilを返すので、保存がうまくいかなかったです。
count = 0
allow(user).to(receive(:save!)) do
count += 1
raise.new(ActiveRecord::RecordNotSaved, "保存失敗") if count == 1
end
これを発展させ、
-
once
にチェーンさせる -
return self
とかself.save!
とかで二回目以降に何かしらオブジェクトを返す
とか試行錯誤してもうまくいきません。(原理的にも無理そう)
調べていくうちに、スタブは「後定義勝ち」と言うことを知ったので、
最終的に中で再定義すると言う荒技(?)
# user.save!を失敗させる
allow(user).to receive(:save!) do
allow(user).to receive(:save!).and_call_original
raise.new(ActiveRecord::RecordNotSaved, "保存失敗")
end
こうすると二回目以降にはブロック内で定義されたand_call_original
が実行されると言う・・・
後書
期待通り動く!
けど可読性悪い・・・
コードが重複しているので全然スマートでないような
Rails初心者なんで、他にスマートなやり方あれば教えてください!
(そもそもこんなテスト必要な設計が悪い、とか言うそもそも論もある)
以下、参考にさせていただきました。ありがとうございます。
rspec で「一回だけ」例外発生させたい