In our Rails 4.0 application using MySql we use rspec together with the database_cleaner gem configured with strategy :transaction to cleanup our database for every test case. If we have custom transactions, which should be rollbacked, it doesn't work.
Without database_cleaner gem and just using the standard way:
config.use_transactional_fixtures = true
everything works as aspected. But for running feature tests with JavaScript we need database_cleaner to change the fixture deletion strategy to :truncation.
How can we use database_cleaner together with custom transactions and why does it differ to the standard rspec transaction strategy?
The problem is that database_cleaner calls ActiveRecord.rollback at the end of a test -- which you also use in your code. InnoDB/MySQL does not support true nested transactions so nested transactions in code are not treated truly as transactions unless they are explicitly called to do so.
Consider this block (from the ActiveRecord docs):
User.transaction do
User.create(username: 'Kotori')
User.transaction do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
What do you expect to happen after the Rollback call? You expect it to roll back the Nemu user, and leave you with Kotori, right? Well, what actually happens is that Kotori and Nemu are both created. The Rollback doesn't trigger because you're in a nested transaction (and AR only cares about the parent, currently) and the parent transaction (which has an actual db transaction) does not see the Rollback call -- as it is called in an isolated block. It's weird.
Solution:
Klass.transaction(requires_new: true)
If you set requires_new, ActiveRecord will use or pseudo-use a nested transaction (for Postgres it'll make nested transactions, for MySQL/InnoDB it will make savepoints). When you call a rollback with ActiveRecord, it'll figure out its scope and issue the proper rollback.
The other solution is to use truncation or deletion as a strategy for your tests that involve transactions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With