2019-02-11

RSpec with Railsでテスト時のデータはどのように削除されているか

RailsアプリでRSpecを使うと通常example実行のたびにデータが削除されるようになっているが、これが具体的にどのように行われているかについて。

検証環境は以下の通り。

  • ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin16]
  • rails 5.2.2
  • rspec 3.8.0
  • rspec-rails 3.8.2
  • SQLite 3.16.0

use_transactional_fixtures

RailsでRspecを使うとき、 bundle exec rails generate rspec:install を実行して初期設定をすることになると思うが、そのときに spec/rails_helper.rb という設定ファイルも同時に作られる。

このファイルにデフォルトで次のような設定が記載されている。

RSpec.configure do |config|
  # ..省略

  config.use_transactional_fixtures = true

  # ..省略
end

この use_transactional_fixtures という設定項目が true になっていると、exampleのたびにデータが削除されるようになる。

削除の仕組み

use_transactional_fixtures という設定名からは想像がつきにくいのだが、1つのexampleがデータベースのトランザクションの中で実行されるようになり、exampleの終わりにロールバックされ、データが消えるという仕組みになっている。

テストを実行したときの log/test.log を見るとその様子がよくわかる。

(0.1ms)  begin transaction
(0.1ms)  SAVEPOINT active_record_1

// ここでexampleが実行。テスト内容に応じた各種SQLが出力される

(0.1ms)  RELEASE SAVEPOINT active_record_1
(0.5ms)  rollback transaction

exampleの実行前に begin transaction、終わりに rollback transaction というログが出力されている。

before(:each) と before(:all)

これは本題とは関係無いが、関連知識として知っておいた方が良いと思ったのでついでに書く。

  • before(:each) ブロックに書いた事前処理は先のrollbackの対象となり、明示的に削除処理を書かなくても自動で削除される。
  • 一方、before(:all) ブロックに書いた事前処理はtransactionの外側で実行されるため自動では削除されない。必要に応じて明示的に事後処理を書く必要がある。

Tips

use_transactional_fixtures のエイリアスとして、use_transactional_examples も使えるようになっている。
https://github.com/rspec/rspec-rails/blob/5a9182b24c7370ba293fcdd13d7ce01e6d24cd2e/lib/rspec/rails/configuration.rb#L65

こちらの方が名前としてわかりやすいね。

参考リンク