Isolated fixtures: separate fixtures for each of your tests
I’d like to describe and provide code for what I’m calling “isolated fixtures”. Isolated fixtures allow you to have an independent and separate set of fixtures for Test::Unit::TestCase in your Ruby on Rails project.
I’d already run into the situation a couple of times where adding info for my new test into existing fixtures broke existing tests, either due to assumptions of about the fixture data, or because multiple tests “borrowed” fixtures from previous test.. According to some other pieces I’ve read on the net, the situation would only get worse as our test suite grew (and ours does grow). So, instead of becoming adept at the sort of limbo-dancing required to finesse new tests into the testsuite, I decided to come up with a way to let each set of tests have their own fixtures.
Now, I don’t know if this is considered a hack, or another demonstration of the flexibility and power that is Ruby on Rails. I’m sure you’ll all let me know.
This works for me with Rails 1.1.6. I have no idea if it will work in 2.0. (2.0 might even have this built in for all I know!)
Usage
Let’s assume you have a test class called AccountControllerTest. In this test where you normally use fixture instead user isolated_fixture instead:
isolated_fixture :customers, :accounts
Instead of putting the fixture data into test/fixtures it will create a subdirectory there based on the name of the test class. In this case the path of the directory and the fixture data will be:
test/fixtures/account_controller/accounts.yml test/fixtures/account_controller/customers.yml
Code
Here is the code. Put it in your test/test_helper.rb, or do what I did and put it in test/test_helper/fixtures.rb and require it into test_helper.rb.
class Test::Unit::TestCase
## Each subclass needs its own copy 'fixture_path' so replace
## the existing single class variable.
remove_class_variable :@@fixture_path
class << self
remove_method :fixture_path
remove_method :fixture_path=
end
class_inheritable_accessor :fixture_path
self.fixture_path = RAILS_ROOT + "/test/fixtures/"
## Add specialized fixture class methods and alias them.
def self.standard_fixtures(*table_names)
self.fixture_path = RAILS_ROOT + "/test/fixtures/"
self.fixtures_without_fixture_path(table_names)
end
def self.isolated_fixtures(*table_names)
fixture_subdir = self.to_s.ends_with?("Test") ? self.to_s[0..-5].underscore : self.to_s.underscore
self.fixture_path = RAILS_ROOT + "/test/fixtures/#{fixture_subdir}/"
self.fixtures_without_fixture_path(table_names)
end
class << self
alias_method :fixtures_without_fixture_path, :fixtures
alias_method :fixtures, :standard_fixtures
alias_method :original_method_added, :method_added
end
def setup_with_fixtures_and_with_fixture_path
setup_with_fixtures_and_without_fixture_path
end
## Provide a default 'setup' method.
alias_method :setup_with_fixtures_and_without_fixture_path, :setup_with_fixtures
alias_method :setup, :setup_with_fixtures_and_with_fixture_path
## Handle the case where the user provides a 'setup' method for the tests.
def self.method_added(method)
case method.to_s
when 'setup'
unless method_defined?(:setup_without_fixtures_and_without_fixture_path)
alias_method :setup_without_fixtures_and_without_fixture_path, :setup
define_method(:setup) do
setup_with_fixtures_and_with_fixture_path
setup_without_fixtures_and_without_fixture_path
end
end
else
self.original_method_added(method)
end
end
end
Drop a line and let me know if you found this useful, or if you found a bug. I’ll post any updates.
Update
Here is a slide presentation covering the same material.
3 Comments so far
Leave a reply



[…] Aron will talk about the regression testing infrastructure we use to validate all of our markup. I’ll briefly go over another component of our Rails testing environment that we call Isolated Fixtures. […]
[…] You can find Aron’s presentation here, and mine here. […]
[…] A single set of fixture data just breaks down after you’ve created a certain number of tests. I’ve talked about this before and it’s what caused us to create Isolated Fixtures. So, we innovated our way around that. […]