ActiveRecord allows you to start transactions that will be rolled back in the event of an error.
A good example is importing records from a CSV file. If you want the entire import to roll back if any of the rows fail to import, you could write your code like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def import_csv csv_file = params[:csv_file] begin Record.transaction do fastercsv = FasterCSV.new( csv_file ) while row = fastercsv.readline foo, bar = row Record.create!( :foo => foo, :bar => bar ) end end redirect_to success_action_path rescue # do something with the error flash[:error] = "CSV import failed" redirect_to import_path end end |
The Record.create! call will throw an ActiveRecord::InvalidRecord error if one of the rows can’t be saved. Then the rescue block catches the error and reports it to the user instead of showing them an ugly 500 error (or, worse, a corrupted import).
However, this doesn’t play nicely with your tests.
You’d like to do something like this:
1 2 3 4 5 |
def test_import_csv_failure assert_no_difference Record :count do post :import_csv, :csv_file => fixture_file_upload('files/invalid.csv') end end |
But this won’t work, because running the test starts a transaction, and ActiveRecord doesn’t support nested transactions. There’s been a patch open on this problem for 9 months, but no action has been taken.
I was able to work around the problem by turning off transactional fixtures for the entire test case class.
1 2 3 4 5 6 7 8 9 |
class MyTest < Test::Unit::TestCase self.use_transactional_fixtures = false def test_import_csv_failure assert_no_difference Record :count do post :import_csv, :csv_file => fixture_file_upload('files/invalid.csv') end end end |
This makes the test run slower, but now it passes. If you’re feeling adventurous, you can install the ActiveRecord nested transactions plugin.
Lots of people have hit this problem. Jerry Kuch blogged about it in January 2006 and ticket 5457 was filed back in June. But hopefully this post will help someone else figure out the problem.

