8
2

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-retryを導入しようとしたらaggregate_failuresによって動作しなかった話

8
Posted at

概要

RSpecのオプションaggregate_failuresとretryを同時に設定すると動作しなかったため、検証内容と対策を記載します。

aggregate_failures

通常、expectの結果がエラーとなった場合はそこで検証が終了しますが、オプションaggregate_failuresを付けた場合は最後まで検証を続けてくれるようになります。
aggregate_failuresは標準で使用可能です。

以下の解説がとてもわかりやすかったです。

RSpecでテストをまとめて検証する方法
https://qiita.com/yshz/items/21162a9ffeb51fbcbe36

retry

オプションretryはテストが失敗した時、成功するまで指定回数だけ再実行するというものです。
retryはrspec-retryの導入によって使用可能となります。

GitHub
https://github.com/NoRedInk/rspec-retry

以下の解説がとてもわかりやすかったです。

rspec-retryを使ってテストを再実行するときの設定方法
https://daipresents.com/2017/rspec-retry/

今回問題となったこと

rspec-retryを導入したところ、それが機能しない場合がありました。
調査した結果、retryとaggregate_failuresを同時に設定した場合、リトライされないとわかりました。

検証

検証に使用したコードは以下です。
「1は2と等しいか?」という必ずエラーとなるテストを書いて実行しています。

it '1回しか検証されない場合', :aggregate_failures, retry: 2 do
  # aggregate_failuresをメタデータとして書いた場合はリトライされない
  expect(1).to eq(2)
end

it '2回検証される場合 その1', retry: 2 do
  # aggregate_failuresをブロックとして書いた場合はリトライされる
  aggregate_failures do
    expect(1).to eq(2)
  end
end

it '2回検証される場合 その2', retry: 2 do
  # aggregate_failuresが未設定の場合はリトライされる
  expect(1).to eq(2)
end

原因

ざっくりと原因を説明すると、

  • retry側「テストは成功した?(失敗なら再実行するよ)」
  • rspecの本体側「(aggregate_failuresのために、エラーを一時的に退避させているから)失敗してないよ」

というような会話をしている状況でした。

このエラーを退避させるかのような動作についてはGitHubにissueがあり、「これは仕様」という結論となっていました。
https://github.com/rspec/rspec-core/issues/2289

また、このissueには対策が記載されていましたが、それをretryに適用することは、私がコードを確認した限りではできないようでした。

aggregate_failures + retry という組み合わせは相性が悪いと結論付け、その場合の対策を施すことにします。

対策

検証コードのように、aggregate_failuresをメタデータでなくブロックで設定してあげれば、retryオプションが正しく動作するようになります。

ですが私のプロジェクトの場合、以下のコードによってaggregate_failuresが全てのテストに適用されていました。

RSpec.configure do |config|
  config.define_derived_metadata do |meta|
    meta[:aggregate_failures] = true unless meta.key?(:aggregate_failures)
  end
end

ちなみに、これはRSpecの公式にもほとんど同じコードがあるので正しい使い方なのだと思います。
https://relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures#enable-failure-aggregation-globally-using-%60define-derived-metadata%60

そのため、以下に修正することによってaggregate_failuresとretryが同時に適用されないようにして運用することにしました。

RSpec.configure do |config|
  config.define_derived_metadata do |meta|
    meta[:aggregate_failures] = true unless meta.key?(:aggregate_failures)
    meta[:aggregate_failures] = false if meta.key?(:retry)
  end
end

最後に

理想的な対策までたどり着けませんでしたが、「相性問題だからしょうがない」と言えるところまで調査できたと感じています。
実用レベルではほとんど問題ないと思いますので、これでしばらく様子を見てみたいと思います。

8
2
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
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?