rspec で「一回だけ」例外発生させたい


久々にちょっとレアケースな感じの rspec 書いたら上手く動かずハマりました
一回目は例外出したいけど二回目からは出したくない、的な。
具体的には DynamoDB の WriteCapacity を超えてしまった際に時間を少し開けてリトライさせる処理のテストです。

テスト対象のコード(簡略化したもの)

def call
  hoge.fuga
rescue PiyoError
  retry
end

上手く行かなかったやつ。

before do
  allow(hoge).to(receive(:fuga)).once do
    raise PiyoError
  end
end

it do
  subject
  expect(line_user).to have_received(:update_latest_talk_cache!).twice
end

動作的には 1 回だけ raise されるようになるが、 expect で 1 回しか実行していない、と怒られる。
恐らく fuga が 1 回目の実行分しか spy オブジェクトにならないんだと思う。

結局こうした。

before do
  @count = 0
  allow(hoge).to(receive(:fuga!)) do
    if @count.zero? # NOTE: 一度目の実行のみ例外を出す
      @count += 1
      raise PiyoError
    end
  end
end

it do
  subject
  expect(line_user).to have_received(:update_latest_talk_cache!).twice
end