WEBサービス創造記

WEBサービスを作ったり保守したりしてる人のメモブログです。

DatabaseCleanerの設定

   

DatabaseCleanerについて

テスト前後のデータベースのデータを処理する用途によく使われるGem。

テストは意図しない結果にならないように、そのテストに必要最低限なデータだけを準備して実行する必要がある。
例えば、あるテストケースを実行する際に他のテストケースで生成されたデータの影響を受けてテストケースが失敗するということを避けなければならない。

database_cleanerを利用すれば、テスト前にDBを初期化・テスト後にテストで作ったデータの後始末を実行することができて、テストの独立性を担保することができる。

DatabaseCleaner/database_cleaner

設定項目

基本公式にある設定を踏襲するのがいいと思う。

下記に個々の設定項目の意味を見ていく。

DatabaseCleaner.start / DatabaseCleaner.clean

記述例

RSpec.configure do |config|
  config.before(:each) do
    DatabaseCleaner.start
  end

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

意味

DatabaseCleaner.startで、DatabaseCleanerによるデータの管理を開始する。 DatabaseCleaner.cleanを実行すればデータが削除される。したがってこれらをセット使ってデータを削除するタイミングを定義することになる。

上記例ではテストケースを実行するたびにデータを削除する(before(:each)で作成したテストデータは消去)。
※ただし、これだけだとbefore(:all)などの:each以外のタイミングで作成したテストデータは消去されないのでその辺りは適宜設定が必要。

DatabaseCleaner.strategy

記述例

RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
  end
end
DatabaseCleaner.strategy = :truncation, { only: %w(widgets dogs some_other_table) }
DatabaseCleaner.strategy = :truncation, { except: %w(widgets) }

意味

データ削除の方式。 :transaction もしくは :truncation を指定する。 exceptやonlyで適用するテーブルを除外したり限定したりすることもできる。

:truncationの方が高速なので、トランザクション処理が必要ない箇所ではそちらを使うように制御する場合もある。
下記はその好例。

RSpec.configure do |config|
  # 各テスト実行前に実行される
  config.before(:each) do |spec|
    # multithread が trueのときは truncation
    if spec.metadata[:multithread]
      DatabaseCleaner.strategy = :truncation
    else
      DatabaseCleaner.strategy = :transaction
    end

    DatabaseCleaner.start
  end
end

Railsでマルチスレッド処理のテストを書く際の設定 | 69log

transactionにすると、手元の環境では以下のように大きな差がでました。
– truncationの場合: 9m 17s
– transactionの場合: 28s
transactionが使える場合はtransactionを使ったほうがいいかと思います。

RSpec – database_cleaner transactionの設定 – Qiita

DatabaseCleaner.clean_with

記述例

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

意味

上記のように記述することで、clean 実行時に処理する対象から除外するテーブルを指定することができる。

seedなどで作成するマスターデータなど都度生成コストがかかるものを指定したりすることがある。

その他問題

AUTOCOMMITされたデータが消えない

ただ、transaction strategyで実行している場合はDatabaseCleaner.clean実行後もAUTOCOMMITされたデータが削除されません。

そのため「個別にテストを実行すると成功するのにrake specでは失敗する」といったことが起きてしまう場合があります。

RSpecでDatabaseCleanerを使っているのにAUTOCOMMITされたデータが残る場合の対処|江の島エンジニアBlog

RSpec設定との競合

RSpecのconfig.use_transactional_fixtures = trueDatabaseCleaner.start/cleanが競合することがある。

参考 RSpec + database_cleaner で永続的なマスターデータを扱う – 彼女からは、おいちゃんと呼ばれています

 - Ruby on Rails ,