fv17の日記 - Coding Every Day

Webエンジニアの備忘用ブログです。主にWeb界隈の技術に関して書いています。

RSpecがRandom failする場合の調査方法や対処方法

Random failする理由は様々ですが、下記を見てみます。
それぞれの詳細は別途ググって見てください。

  1. feature specでCapybara::ElementNotFound等で落ちる場合
  2. データ不整合等の理由で、特定の実行順で落ちる場合

1. feature specでCapybara::ElementNotFound等で落ちる場合

原因

  • ajaxの処理が未完了
  • 要素の描画が未完了
    など

対策

  • その1. Capybaraのwait時間を伸ばす

デフォルトで2秒しか待たない設定なので、それ以上に伸ばします。
下記の設定だとグローバルに待機時間が伸びます。テスト毎に設定も可能

Capybara.default_max_wait_time = 5
  • その2. rspec-retry gemを導入

テストが失敗した場合に自動リトライさせます。
https://github.com/NoRedInk/rspec-retry

  • その3. feature specからsystem specに移行する

system specにすると、処理が高速化し、エラーで落ちにくくなります。
database cleanerが不要になるなど、処理時間も半分近くになるのでオススメ。

2. データ不整合等の理由で、特定の実行順で落ちる場合

原因

  • テストの実行順により、データの状態が変わり落ちる
  • データが存在しない、すでに同じキーでデータが存在するなど
  • ActiveRecord::RecordNotUniqueが出たりするので比較的分かりやすい

調査

まず、どの順番になると落ちるか?を特定するため、エラーを再現させます。
RSpecの結果に表示されている Randomized with seed #### と出力されるseed番号を実行時に指定することで、同じ順序でのテスト実行が可能です。

rspec --seed 12345

これによりCI上で発生していたエラーがローカルでも再現できるはずです。
で、エラーを発生させるために毎回上記を実行していると非常に時間が掛かるので、bisectオプションを付けることでエラーが発生する範囲を特定できます。

rspec --seed 12345 --bisect

上記を実行すると次の表示が出力されます

The minimal reproduction command is:
  rspec 'hogehoge' --seed 12345

出力されたコマンドを実行すると、最短でエラーを再現できるようになります。
エラーの再現ができるようになったので、後はデバッグして問題を解決するだけです。

参考: seedやbisectオプションの詳細は下記を参照

実用的な新機能が盛りだくさん!RSpec 3.3 完全ガイド - Qiita

その他の調査方法

Circle CIの場合、rerun with sshすることでCI上の環境でデバッグ可能。
binding.pryを埋め込んだりもできる。