【Rails】DatabaseCleaner導入でテストが遅くなった話
現在仕事のrailsプロジェクトで、feature specの導入のところでdatabase_cleanerの設定でいろいろ時間を費やしたため、時間を費やした箇所についてまとめておこうと思います。 その時のRailsバージョン/Rubyバージョン:3.2.11/1.9.3。古い!
DatabaseCleanerとは
github.com databaseのお掃除をしてくれるgem。主にrspecやminitestの際に、「テストが終わったらテストデータを全部消したい」みたいなモチベーションで使われる。
使い方
簡潔にまとまっている記事がたくさんあるので省略します。
【Ruby on Rails】Database Cleanerによるテストデータの消去 | Developers.IO
RSpec + database_cleaner で永続的なマスターデータを扱う - 彼女からは、おいちゃんと呼ばれています
困ったところ(今回のメイン)
DatabaseCleanerを使いはじめると、急にテストが遅くなる
rspecである程度テストが書いてあるプロジェクトにDatabaseCleanerを入れてみたのですがテストが急に遅くなりました。
今回実施した導入方法は以下になります。
* Gemfile
gem 'database_cleaner'
- spec/spec_helper.rb
RSpec.configure do |config| # これより前の設定は省略... # config.use_transactional_fixturesはコメントアウト # config.use_transactional_fixtures = true config.before(:suite) do DatabaseCleaner.strategy = :truncation end config.before(:each) do DatabaseCleaner.start end config.after(:each) do DatabaseCleaner.clean end
log/test.log
をみていると、同じクエリログが何回も出ていました。よくよく調べてみるとspec/fixturesディレクトリで定義しているfixtureをテストのたびにDBにinsertしてtruncateしているようでした。DatabaseCleanerを入れるまでは以下の設定が入っていたのでfixtureは一番最初にinsertして、テスト毎に入れたり消したりされることはありません。DBのクリーンはrspecに入っている機能で行う模様です。
config.use_transactional_fixtures = true config.global_fixtures = :all
解決方法
spec/spec_helper.rbのDatabaseCleanerのstrategyをtransactionにしました。
# これより前の設定は省略 # config.use_transactional_fixtures = true # config.global_fixtures = :all config.before(:suite) do DatabaseCleaner.clean_with(:truncation) dir = "#{Rails.root}/spec/fixtures" Dir["#{dir}/*.yml"].map { |f| f[(dir.size + 1)..-5] }.each do |fixture_file| ActiveRecord::Fixtures.create_fixtures(dir, fixture_file) end end config.before(:each) do DatabaseCleaner.strategy = :transaction end config.before(:each) do DatabaseCleaner.start end config.after(:each) do DatabaseCleaner.clean end
要約すると、①fixtureは自力で入れる。②DatabaseCleanerのstrategyをtransactionにする。
です。rspecのtransactionを使うとfeature specは上手く動かない場合があるのですが、DatabaseCleanerの場合はひとまず大丈夫そうでした。
テストの速度も以前通りになりました。
その他
他のプロジェクト(Rails4系)でtruncate→transactionの設定を試したところ、残念ながらテストは早くならず逆に遅くなってしまいました。
テーブル数、データ量等の違いによって結果が異なりそうなので、ベストな設定はこれ!というものはなさそう。実は、パーフェクトRuby on Railsにはfeature spec用の設定としてはまた若干違った設定が書かれています。
他にもいろいろ設定があるようで、DatabaseCleaner.strategy = :truncation
に:except
というオプションを与えてtruncateするテーブルを制限する方法もあるようです。
悩んでるポイントはみんな同じ!?「Rubyistのためのテストコード相談会」の質疑応答まとめ - give IT a try
database_cleaner transactionの設定 - Qiita
まとめ
rspecやDatabaseCleanerの設定にはいろいろあるので、テストの量やDBの状態に応じてベストな設定を模索していきましょう。
もっと良い設定があればぜひ教えてほしいです!