Rails4での話。(3以下は未確認。おそらくRails3でも同じ)
tl;dr
- rails4ではテスト実行時に
transactional_fixtures
のせいでafter_commit
が呼ばれない - test_after_commitを使えば解決できる
- rails5では修正されている
問題
test実行時にActiveRecord::Callbacks
のafter_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
これまでに書いた大半のことが書いてある素晴らしいやつ