Ruby
Rails4

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

More than 3 years have passed since last update.

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


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

http://anti-pattern.com/transactional-fixtures-in-rails