LoginSignup
16
13

More than 5 years have passed since last update.

Rails4でのテスト実行時にafter_commitを呼ぶ

Posted at

Rails4での話。(3以下は未確認。おそらくRails3でも同じ)

tl;dr

  • rails4ではテスト実行時にtransactional_fixturesのせいでafter_commitが呼ばれない
  • test_after_commitを使えば解決できる
  • rails5では修正されている

問題

test実行時にActiveRecord::Callbacksafter_commitが呼ばれない
そのため、意図通りに動かない・テストが通らないということになる

after_commitのドキュメントには以下のように書いてある
https://github.com/rails/rails/blob/v4.2.5/activerecord/lib/active_record/transactions.rb#L235-L237

Note that transactional fixtures do not play well with this feature. Please
use the +test_after_commit+ gem to have these hooks fired in tests.

意訳すると、

テスト実行時にtransactional fixturesを使ってるとafter_commitが呼ばれないのでtest_after_commitを使ってね。

transactional_fixtures

http://anti-pattern.com/transactional-fixtures-in-rails から
Transactional fixtures simply wrap each test in a database transaction and start a rollback at the end of it, reverting the database back to the state it was in before the test.
If the database was empty before the test, it will roll it back and be empty after the test. If the database had preloaded test data (like fixtures) in it before the test, it will roll it back and have that same data after the test.
Even though this feature is unrelated to fixtures themselves, fixtures are the default way to create test data in Rails, hence the name “transactional fixtures”.

意訳すると、

Transactional fixturesはテストごとにDBのtransactionを開始しテスト終了後にDBをrollbackされ、DBはテスト実行前の状態に戻る。
テスト実行時にDBが空っぽならば空っぽに戻るし、fixturesなどでデータが用意されていれば同じデータを持った状態に戻る。
この機能はfixturesとは関係ないが、fixturesがrailsのテストデータを用意するデフォルトの方法なので、transactional fixturesという名前が付いている

つまり、database_cleaner等と同等の機能。
(rails4ではdataase_cleaner等は基本的には必要ない。ただ、mongodbなどには対応していないので、そういう時にはdatabase_cleaner等を使う必要がある)

  • テスト実行ごとにtransactionを開始してrollbackをしている
  • つまりDBにcommitをしていない
    • たぶん!(実装ちゃんと見てないのでわからないが、DB的にはcommitしていないだけでなのかなと思う)
  • そのためafter_commitが呼ばれない
    • (そのためかどうか、は実装ちゃんと見てないのでわからない)

という感じなのだと思う。

解決策

  • railsのドキュメントにある通り、 test_after_commitを使う
  • transactional_fixturesをoffにしdatabase_cleanerを使い、DatabaseCleaner.strategy:transactionではなく:truncationにする
    • 本当にafter_commitが呼ばれるかは未確認。動く気がしているだけ

Rails5

rails5ではtransactional_fixturesを使っていてもafter_commitが呼ばれるようになっている
あと、transactional_fixturesではなくてtransactional_testsという名前になっている
関連PR
https://github.com/rails/rails/pull/18458 Support after_commit callbacks in transactional fixtures
https://github.com/rails/rails/pull/19273 ‘test_after_commit’ gem is not require in Rails 5, remove note from doc ...
https://github.com/rails/rails/pull/19282 Renaming transactional fixtures to transactional tests

これまでに書いた大半のことが書いてある素晴らしいやつ

16
13
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
16
13