0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【RSpec】中途半端なデータを残してしまってエラーになる現象について&予防策と暫定的な対応

Last updated at Posted at 2024-12-24

当記事の解決策は最適ではないように思いますので参考程度にしてください

はじめに

  • 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/

※ここでいう exampleit/example/specifyexample と思われます

  • ただ私の場合、この設定を有効にしていたが 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 がロールバックしてくれているはずなので余計なことをしているような気がする
    • そもそも削除をしないためにロールバックしているわけで

参考資料

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?