当記事の解決策は最適ではないように思いますので参考程度にしてください
はじめに
- RSpec の Capybara でシステムテストを行った際にレコードが重複して生じるエラーで困った方々に向けております
概要
- Rails の Web アプリを RSpec の capybara でシステムテストを行った際にエラーが生じた
- クリアしていたテスト項目でエラーが突如検知されるようになった
- これについては「トラブルの原因」で後述
テストで生じたエラー
実際に生じたエラー
ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "XXXXX_pkey"
DETAIL: Key (id)=(1) already exists.
- レコードが既に存在しているよという旨なのですが、クリアしていたテスト項目に唐突にこのエラーが生じるようになりました
調査したこと1 - RSpec の設定関連
-
config.use_transactional_fixtures
の設定を有効にすることで全てのテストを実行後にロールバックすることで"実質的な削除"を行っていることになる-
destroy
しているではなく全てのレコードをロールバックして初期化している
-
RSpec.configure do |config| config.use_transactional_fixtures = true end
(直訳)「この設定の名前は少し誤解を招きやすいです。Rails で実際に意味するのは、「トランザクション内のすべてのテスト メソッドを実行する」ということです。rspec-rails のコンテキストでは、「トランザクション内のすべての例を実行する」という意味になります。
アイデアは、各 example をクリーンなデータベースで開始し、その example に必要なデータを作成し、example の最後にトランザクションをロールバックするだけでそのデータを削除するというものです。」
引用:https://rspec.info/features/6-0/rspec-rails/Transactions/
※ここでいう example
は it
/example
/specify
の example
と思われます
- ただ私の場合、この設定を有効にしていたが
binding.pry
で確認を行った所データが残っているのが確認できた
調査したこと2 - database_cleaner
という gem
database_cleaner
(https://github.com/DatabaseCleaner/database_cleaner)
- その名の通りデータベースをクリーンにしてテストを実行できるようにする
-
use_transactional_fixtures
は「example を最後まで実行したタイミング」なので、どういったタイミングで DB を整えるかを指定できる
- ものは試しと思い導入してみたものの先のエラーが先行して私の場合は上手く機能しませんでした
- 導入するのであれば RSpec の導入と併せてやっておくべきでした
- そもそも
use_transactional_fixtures
が細かにロールバックしてくれているので、この gem が今回のエラーの解決策にはならないと判断
【非推奨】解決策
他に解決策があるかもしれませんので、半ば強引に解決したので参考程度にしてください
実際に生じたエラー
ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "XXXXX_pkey"
DETAIL: Key (id)=(1) already exists.
- このエラーから分かるように「登録しようとしているデータが既に存在してしまっている」のが問題
- これを削除してやれば良いと考えた
-
before
ブロックで指定したモデルのレコードをテスト実行前に整理した
追加したコード
before do
User.destroy_all # テスト前にデータを削除
Post.destroy_all # テスト前にデータを削除
end
- 以下のように追加しました
- 結果として無事解決
spec/system/sample_spec.rb
require "rails_helper"
RSpec.describe "サンプルテスト", type: :system do
before do
User.destroy_all # テスト前にデータを削除
Post.destroy_all # テスト前にデータを削除
end
#####【中略】#####
end
今回のトラブルの予防策
トラブルの原因
use_transactional_fixtures
は「最後までテストを実行した後にロールバック」するため強制中断が悪手だったと推測
- 私の今回の失敗として 100 件程度のテストを実行していたため途中でテストを強制的に中断させてしまった のがトラブルの原因だったと思われます
- エラーが出た時点で確認したかったため
Ctrl + C
で無理矢理中断
- エラーが出た時点で確認したかったため
- これにより前回テスト時のデータが残ってしまってエラーが生じた
RSpec 実行時の予防策
使用していたコマンド
bundle exec rspec
- 「テストケースが多い場合に最後まで実行し切るのを待つ」のは中々大変ですので、予防策として以下のように
--fail-fast
オプションを付ければ テストがクリアできなかった場合に中断 してくれます
改善後のコマンド
bundle exec rspec --fail-fast
所感
- 当記事のエラー以外にも問題を抱えていたのですが、今回の解決策を行った所ソレも解決したため私の場合は解決策として上手く機能しました
- ただ毎度データを削除する操作をしないために RSpec がロールバックしてくれているはずなので余計なことをしているような気がする
- そもそも削除をしないためにロールバックしているわけで
参考資料