おいちゃんと呼ばれています

ウェブ技術や日々考えたことなどを綴っていきます

RSpec + database_cleaner で永続的なマスターデータを扱う

例えば EC サイトを Rails でつくっていて、RSpec + database_cleaner でテストを行っているとして。商品カテゴリーデータ等の永続的なマスターデータを扱いたいときどうするかって話です。

最近 paperboy&co. に入社された @ さん(『パーフェクト Ruby』の共著者!)がヘルプしてくれたのですが、お役立ち情報なので共有しておきます。

なお、使用しているバージョンは下記のとおりです。

設定例

先に設定例を示して、その後に解説をします。

RSpec.configure do |config|
  #...
  
  config.use_transactional_fixtures = true
  
  config.before(:suite) do
    load Rails.root.join('db', 'seeds.rb')
    DatabaseCleaner.clean_with :truncation, { except: %w(categories brands) }
  end
end

解説

(1) database_cleaner の clean する対象から除外

まず下記のように except オプションを使えば、database_cleaner が clean する対象から除外するテーブルを指定することができます。

DatabaseCleaner.clean_with :truncation, { except: %w(categories brands) }

が、しかし、これだけでは、うまくいかなかったので、以下、解説を続けます。

(2) rspec spec だとうまくいくのに、rake spec だとこけていた

当初 db:seed したうえで bundle exec rspec spec だとテストがすべて通るのに、bundle exec rake spec だとこけるテストがありました(マスターデータを取得するテスト)

で、bundle exec rake spec --trace してみたら、db:test:purge で DB が空っぽになっていたことが原因でした。

(3) テストを開始する直前に db:seed を実行する

なので、テストを開始する直前に db:seed を行うことにしました。それが下記の箇所です。

load Rails.root.join('db', 'seeds.rb')

各テストで、利用するマスターデータを都度作成する方法もあると思うのですが、毎回データをつくっては消すというのはコストが高いので、最初に一度だけ読み込むのが良いと思います。

(4) クリーンアップ機能の競合

次に直面したのが、

config.use_transactional_fixtures = true

と、database_cleaner の公式ドキュメントにも書いている下記の設定が競合するという問題。

config.before(:each) do
  DatabaseCleaner.start
end

config.after(:each) do
  DatabaseCleaner.clean
end

結局 config.use_transactional_fixtures = true の方を残しました(逆を残すとうまくいかなかった)

その他

database_clenaer の設定の詳細については、公式ドキュメントを参照してください。

truncation と transaction の違いに言及した日本語情報もあったので、貼っておきます。

あと、Ruby 2.0 以上しかサポートしていないけど、database_rewinder というものもあるようです。

というわけで @ さん、ありがとうございました。あと『パーフェクト Ruby』すごく役に立っています!

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

関連エントリー