Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

rspec-retryを導入しようとしたらaggregate_failuresによって動作しなかった話

More than 1 year has passed since last update.

概要

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

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を導入したところ、それが機能しない場合がありました。
調査した結果、retryaggregate_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_failuresretryが同時に適用されないようにして運用することにしました。

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

最後に

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

ljourm
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away