Close and Go BackBack to Viget

Testing Transactions in Rails

Ben Scofield
Ben Scofield, Development Director, October 24, 2006 3

The standard framework for testing in Ruby on Rails is a thing of beauty; there are folders to drop your unit and functional tests into right off the bat, fixtures and mock objects are expected and encouraged, and when you use scaffolding you get stubs for the common CRUD tests for free. That helps make testing less onerous for developers and, as a result, applications are better tested.

Even Rails isn’t perfect, however, and there are some situations where the testing set-up created out-of-the-box just doesn’t work so well—and, unfortunately, one of those problem situations involves database transactions.

Put simply, transactions are a way to keep your data in a valid state. Say you have a banking application that allows people to transfer money from one account to another. The code might look like this:

andy_account.withdraw(300.00)
ben_account.deposit(300.00)

If all goes well, Andy’s account is $300 lighter, while I’m $300 richer—but, what if the system crashed in the middle, after the withdrawal but before the deposit? When the system comes back up, we’d see that the $300 withdrawal has been lost to the whims of the application. Transactions represent one possible solution to this problem. They run on the database server and make sure that a set of operations either all succeed or all fail. If our current example used transactions, the failure of either operation would cause both to be rolled back, leaving both accounts as they were before the ill-fated transfer attempt.

Clearly, then, transactions can be useful. It turns out, though, that when you use transactions in your code in Rails, the tests don’t always show what you’d expect them to. The problem is that, by default, all tests in Rails are already using transactions behind the scenes (to make loading and unloading test data fixtures as fast as possible). There’s a setting in the TestHelper class that controls this: use_transactional_fixtures. You could just set that to false to fix your transaction tests, but then you’d slow down the entire test suite significantly. A better alternative is to change the setting for the cases that hold transaction tests:


class TransferTest
self.use_transaction_fixtures = false
...
end

If you have transactional and non-transactional tests in a single case, it can make sense to control this setting at an even more granular level, by separating those tests in different test cases:


class TransferTest
...
end
class TransactionalTransferTest
self.use_transaction_fixtures = false
...
end

Happy testing!

Trackback: culann.com » Blog Archive » Ruby on Rails posts on 11/01 at 11:55 PM [...] around I found a simple solution. Turn off this behavior: PLAIN TEXT [...]----- [...] Testing Transactions in Rails Blocking Spam in Rails Applications [...]-----
Aleksander Pohl said on 07/29 at 08:54 PM

I guess there’s a typo in your code:
instead of self.use_transaction_fixtures,
there should be self.use_transactionAL_fixtures (as for Rails 1.2.3).

Sjoerd said on 12/15 at 08:57 PM

Thanks Ben. I spent hours finding out why my test failed… Very helpful post!

p3t0r said on 01/24 at 04:15 PM

I ran some tests, but setting ‘self.use_transaction_fixtures = false’ seems to affect _all_ consequent tests.