Background
For the last several days I have been feeling the pain of Rails fixtures very accutely. Once your application begins to grow, the expense of maintaining your fixtures becomes higher and higher—often there is a ripple effect in which adding objects to test one part of the app causes assertions to come unglued elsewhere, breaking your development rhythm with cycles of application maintenance. Aside from this, authoring many objects manually by editing YAML can get tedious and time-consuming if you need to work with just a few dozen objects.
For example, at Near-Time we track individual visits to entities within the system. The Visit model that represents the individual accesses is relatively trivial, but uses a polymorphic association for the ‘visitable’ association. Maintaining the visits.yml file can be a serious pain in the ass, since all the tables need to be cross referenced and represented as entities. What is a frustrated, lazy hacker to do? Fixture Sets to the rescue!
Introducing Fixture Sets
Fixture Sets is a plugin for Ruby on Rails that provides two powerful new features that make working with fixtures less painful:- Sandboxed groups of fixtures that can be maintained seperately from the main ‘base’ set of fixtures.
- Automatic generation of YAML fixture files from a simple specification file using Active Record finders.
Sandboxed Fixtures
Fixture sets provides a new directory at test/fixture_sets that contains subdirectories with individual groups of YAML files. Instead of declaratively loading each table fixture by fixture using the fixtures method in your tests, you work with an entire group of fixtures at once using the fixture_set class method. Here’s an example:
class FooTest < Test::Unit::TestCase fixture_set :foo
def test_blah
user = users(:blake)
visits = Visit.find(:all)
end
end
And the corresponding fixture set looks like this:
$ ls test/fixture_sets/foo users.yml visits.yml spaces.yml foo_fixture_set.rb
This provides a higher level abstraction than the per table fixtures and frees you up from the agonizing maintenance cycle that can follow defining new objects. Just make a new set and you are off to the races -- you can even reuse primary key ID's across fixture sets.
Automatic Fixture Generation
In addition to providing sandboxed, named groups of fixtures the Fixture Sets plugin also provides a simple, clean mechanism for extracting fixtures from your development (or production) database into a set of YAML fixtures. A generator is provided to make this process even more straightforward. An example is worth a thousand words:
$ script/generate fixture_set foo
exists test/fixture_sets
create test/fixture_sets/foo
create test/fixture_sets/foo/foo_fixture_set.rb
And the example fixture set specification:
FixtureSet.define(‘foo’) do |set| User.active = User.find_by_username(‘blaketest’) set.users = User.active set.spaces = User.active.visitable_spaces set.space_users = SpaceUser.find_by_user(User.active) set.pages = Page.with_scope(Scopings.in_my_spaces) { Page.find(:all) }
end
Now we can generate our fixtures using rake:
$ rake test:fixture_sets:scan
Generating YAML files for unpopulated Fixture Set ‘foo’
Generating fixture for tables ‘users’
Generating fixture for tables ‘spaces’
Generating fixture for tables ‘space_users’
Generating fixture for tables ‘pages’
And there you have it, the Fixture Sets plugin in all its glory. This is still a work in progress – additional rake tasks and other fit and finish still need to be ironed out. But it’s functional and in my humble opinion, quite useful.
Please read the README file for more details: https://secure.near-time.com/svn/plugins/trunk/fixture_sets/README
Installing Fixture Sets
Installation is performed via the standard Ruby on Rails plugin script:
$ script/plugin install https://secure.near-time.com/svn/plugins/trunk/fixture_sets/

RSS
danny said 9/19/06
Interesting and very useful plugin, but… there is a problem when
You have constraints in DB. As You may guess fixture loading order
is then crucial. But don’t worry, here is patched version of
FixtureSets module. Now fixture order can be specified like this:
fixture_set :dashboard, :users, :panel,....
code
module Test module Unit module FixtureSets # Load a fixture set by name. def fixture_set(name, order) fixture_set_path = FixtureSet.path_for_set(name) raise "Unable to find Fixture Set named #{name} [#{fixture_set_path}]" unless File.exists?(fixture_set_path) fixture_files = FixtureSet.fixtures_for_set(name) raise "No fixtures found in #{fixture_set_path}, have you run rake test:fixture_sets:scan?" if fixture_files.empty? @@original_fixture_path = Test::Unit::TestCase.fixture_path Test::Unit::TestCase.fixture_path = fixture_set_path fixture_symbols = fixture_files.map {|f| f.gsub(’.yml’, ’’).to_sym}
end
fixtures(fixture_symbols) end enddanny said 9/19/06
Interesting and very useful plugin, but… there is a problem when
You have constraints in DB. As You may guess fixture loading order
is then crucial. But don’t worry, here is patched version of
FixtureSets module. Now fixture order can be specified like this:
fixture_set :dashboard, :users, :panel,....
Source is here: http://pastebin.com/789863
Bryan said 10/5/06
This looks very useful. Unfortunately, I can’t seem to check out the plugin because Subversion is complaining that it doesn’t support SSL. Is there an alternate way to get at it? I’d rather not have to rebuild svn.
Ryan Norbauer said 3/1/07
Bryan: I couldn’t get script/plugin to work either because of the SSL certificate acceptance requirement. I was, however, able to fetch the plugin by navigating to vendor/plugins and running:
svn export https://secure.near-time.com/svn/plugins/trunk/fixture_sets/
Jeremy said 2/12/09
I like the idea of this plugin very much. I have acquired it via svn checkout. It seems the plugin uses some deprecated stuff. Received:
:0:Warning: Gem::SourceIndex#search support for Regexp patterns is deprecated
during script/generate fixture_set <setname>. Is there is anything newer (possibly updated for Rails 2) out there some where?