I’ve recently worked through a rather frustrating issue that turned out to have a simple solution. I thought I’d share in case others out there were experiencing issues similar to mine. Your ActiveSupport::TestCase tests (the default in Rails) are configured to run in transactions (unless they are explicitly disabled), so normally one can assume that each test runs on a clean slate of data. It’s even supposed to work with factories like FactoryGirl (highly recommend using this gem to make your life easier while testing). As it turns out this isn’t always true: this feature of Rails only works to a point and tends to break with even the simplest cases when running tests in a multithreaded environment.
The Problem
The issue arose when I switched my testing infrastructure over to a combination of Guard and Spork. As a side note, so far I really like this setup as it runs the tests relevant to the change(s) made whenever your files are modified, and these tests are a bit snappier thanks to Spork. After making this switch I noticed that my tests were ending in unexpected states (mostly leaving rows behind). This was causing later, more complex tests to fail when they should not have been failing.
In my case I believe the issue was stemming from the multithreaded environment that is created with Spork (this is how Spork achieves its speed boost), as the tests were working without issue before the change. It is worth mentioning that the default transactional setup of ActiveSupport::TestCase may also fail with complex tests (depends entirely on the test setup). This can cause rows being left behind after a test just as my multithreaded issue did. I’ve never seen this in practice myself but in researching this issue and discussing it online I encountered a few developers who have. Just something to keep in mind.
The Fix
Thankfully the solution for this is really simple! There is a fantastic gem out there called database_cleaner that was created to offer developers more flexibility in this situation. database_cleaner offers a couple of different options for cleaning up changes in your database between tests. Take a few minutes to read about them, as some are more performant than others but won’t work in all situations. For my case I stuck with the transactions option (the most performant) but explicitly wrapped each of my tests in one using the DatabaseCleaner methods instead of relying on Rails to do it for me. Once I installed the gem I simply had to add the necessary DatabaseCleaner calls to my test/test_helper.rb file, and my problems were solved. My test/test_helper.rb now looks like this:
# test/test_helper.rb ENV["RAILS_ENV"] = "test" require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' DatabaseCleaner.strategy = :transaction class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. # # Note: You'll currently still have to declare fixtures explicitly in integration tests # -- they do not yet inherit this setting fixtures :all # Stop ActiveRecord from wrapping tests in transactions self.use_transactional_fixtures = false setup { DatabaseCleaner.start } teardown { DatabaseCleaner.clean } # Add more helper methods to be used by all tests here... endTransactional Tests in Ruby on Rails mifsud.me