Testing Transactions in Rails
Ben Scofield, Former Viget
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 ... endIf 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 ... endHappy testing!