Techioz Blog

Rspec - 追加の gem を使用せずにエラーが発生したときに再試行をテストする方法は?

概要

私のコード


def make_error
  raise StandardError, 'err'
end

class Bob
  def bar(thing)
    puts thing
  end
end

def foo
  ['a', 'b'].each do |x|
    retry_counter = 5
    begin
      bar(x)
      make_error
    rescue StandardError => e
      if retry_counter.positive?
        retry_counter -= 1
        retry
      else
        raise e
      end
    end
  end
end

Rスペックコード

...
it 'adds and retries the emails to the digest store' do
  expect(Bob).to receive(:bar).exactly(6).times.with('a')
  expect{ foo }.to raise_error(StandardError)
end

しかし、エラーが発生しないため、上記のテストは失敗します。最初の期待値を削除すると、エラー期待値の上昇は成功しますが、再試行ロジックもテストしたいと考えています。最初の期待値だけを持っている場合、再試行は行われず、1 回の反復を行った後にコードが終了します。

解決策

コードを実行すると、次の結果が得られます。

Failures:                                                                                                                                             
                                                                                                                                                      
  1) foo adds and retries the emails to the digest store                                                                                              
     Failure/Error: expect(Bob).to receive(:bar).exactly(6).times.with('a')                                                                           
                                                                                                                                                      
       (Bob (class)).bar("a")                                                                                                                         
           expected: 6 times with arguments: ("a")                                                                                                    
           received: 0 times

これは、例外の発生に関するロジックや再試行ロジックとは何の関係もありません。

問題は、bar がクラス Bob のインスタンス メソッドであることです。したがって、foo メソッド内から直接 (bar(x)) を呼び出すことはできません。

メソッドが呼び出されず、依然として例外が発生しているため、仕様の期待は失敗していますが、期待していたものではありません。

NoMethodError:
       undefined method `bar' for #<RSpec::ExampleGroups::Foo "adds and retries the emails to the digest store"

このコードをどのように「修正」すべきかは完全には明確ではありませんが、retry と Expect(….).to accept(:bar).exactly(6).times.with(‘a’ を使用する設定を実証するためです。 ) は実際に機能します。有効なデモンストレーションを次に示します。

def make_error
  raise StandardError, 'err'
end

class Bob
  def self.bar(thing)
    puts thing
  end
end

def foo
  ['a', 'b'].each do |x|
    retry_counter = 5
    begin
      Bob.bar(x)
      make_error
    rescue StandardError => e
      if retry_counter.positive?
        retry_counter -= 1
        retry
      else
        raise e
      end
    end
  end
end

RSpec.describe 'foo' do
  it 'adds and retries the emails to the digest store' do
    expect(Bob).to receive(:bar).exactly(6).times.with('a')
    expect{ foo }.to raise_error(StandardError)
  end
end

余談ですが、これは、StandardError に依存する代わりにカスタム例外クラスを使用することが推奨事項であると考えられる理由を示す好例です。