9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RSpecで一度だけエラーを発生させる

Posted at

背景

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 で「一回だけ」例外発生させたい

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?