Ruby
RSpec

Rspecでエラーが起きたあとの状態をテストするときの問題点と回避案

例外が起きたあとのテストに関してあまり情報がなく、事例もなかったので
広く皆様の意見を聞きたい意図も含めて記事に起こしました。

TL;DR

エラー(正確にはStandardError)が発生する機能に関して、エラー発生後の状態をテストしたいがエラーが発生した時点でrspecが止まってしまう。
そのため、例外の内容をチェックしないのであれば、rescueしたあとに本来やりたい作業を行うのが一番無難な回避策

実コードを交えてた問題点の解説

テスト対象とするサンプルクラス

今回は以下のサンプルクラスに対してのrspecとして例を書いて行きます。

テスト対象のサンプルクラス
class Calculate
  def initialize
    # わかりやすい初期値をいれる
    @result = -9999
  end

  # 10を引数で割る
  def ten_division(number)
    @result = 10 / number
    @result
  end

  def last_result
    @result
  end
end

やりたいこと

ten_division の引数に0を指定した場合に last_resultの戻り値 は初期値の -9999 であることを確認したい。

発生する問題点

何も考えずにそのまま書くと以下のようなコード

rspecのテストコード例
context "0を指定した場合" do
  let(:obj) { Calculate.new }
  it "last_resultで-9999が戻ってくる" do
    obj.ten_division(0)
    expect(obj.last_result).to eq(-9999)
  end
end

ただこの場合、Exceptionがあがって途中でspecが止まってしまう。

rspec実行時のエラー例
  1) 0を指定した場合 last_resultで-9999が戻ってくる
     Failure/Error: @result = 10 / number

     ZeroDivisionError:
       divided by 0

このため後続の expect(obj.last_result).to eq(-9999) が実行されずにエラーでrspecが終了してしまう

回避案

エラーが起きるアクションに関してrescueして、その後期待される要素の取得をして確認をする。

コードで表すと以下の通り

rescueで拾う例
context "0を指定した場合" do
  let(:obj) { Calculate.new }
  it "last_resultで-9999が戻ってくる" do
    begin
      obj.ten_division(0)
    rescue
    end
    expect(obj.last_result).to eq(-9999)
  end
end

一応ワンライナーでも書ける

rescueで拾う例(ワンライナー)
context "0を指定した場合" do
  let(:obj) { Calculate.new }
  it "last_resultで-9999が戻ってくる" do
    obj.ten_division(0) rescue
    expect(obj.last_result).to eq(-9999)
  end
end

各種関連リンク

サンプルコード

exception後の挙動を確かめるrspec
※ 本ページでは紹介していない、errorそのもののテストに関しても記載しています。

参考サイト

編集後記

一応この記事を書いた経緯的なものは以下に。
Errorが起きた後の状態を調べるrspecの書き方が全然見当たらないので書いた - コード日進月歩