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 に依存する代わりにカスタム例外クラスを使用することが推奨事項であると考えられる理由を示す好例です。