The main problem with fixtures, for me, has always been how unfun they are. They literally suck the fun out of anything they’re around. You throw them in your test/ directory, then suddenly testing is, like, work. But it’s not work, dammit. This is Ruby, dammit.
So, keep them fun. Write your fixtures in Ruby.
Step 1: FixtureScenarios
This plugin allows you to create ‘scenarios’ which are collections of fixtures and ruby files that represent a context against which you can run tests.
Pretty cool. Instead of just test/fixtures, you can have (for instance), test/fixtures/users and test/fixtures/beta. Then, in your test class, you call scenario :users in place of the normal fixtures :users, :posts, :images call. This will pull all the YAML files from test/fixtures/users into your test database. When you need a completely separate set of fixtures for another feature, it’s as easy as scenario :beta.
Scenarios can also be nested. You can create a test/fixtures/users/banned directory filled with YAMLy files and call it with scenario :banned. This’ll pull in both the test/fixtures/users fixtures and all the test/fixtures/users/banned fixtures. It will also, by default, pull in all the test/fixtures fixtures.
The real beauty of this is when you load up your code six months down the road, recently returned from your jaunt up Everest, and need to add a feature. Fast! Just add a new scenario—forget about worrying on your old tests, they won’t be affected.
This plugin is also great when BDDin’ with test/spec or RSpec. You can have separate contexts in one spec file, all using different fixture scenarios as needed. Maybe you want to assert hardcore pagination behavior—you can load up 10,000 records! But only for that one scenario, leaving your other scenarios sane.
$ script/plugin install http://fixture-scenarios.googlecode.com/svn/trunk/fixture_scenarios
Step 2: FixtureScenarioBuilder
Okay, like I said, we need to write our fixtures in Ruby.
By default, FixtureScenarios will load any Ruby files it finds in your fixture directories. Cool, but not really sufficient. We need transactional fixtures, we need the database cleared out after test runs, we need a clean database when tests start. We really need all the cool features YAML fixtures offer.
And we can get them, with FixtureScenarioBuilder. This is an add-on to Tom’s FixtureScenarios plugin. AKA you need FixtureScenarios for it to work.
$ script/plugin install \ svn://errtheblog.com/svn/plugins/fixture_scenarios_builder
Step 3: scenarios.rb
After you’ve installed the plugin, create a scenarios.rb file and place it in your test/fixtures directory. It is in this file you build your fixture scenarios. In Ruby.
scenario :blog do %w( Tom Chris Kevin ).each_with_index do |user, index| User.create! :name => user, :banned => index.odd? end end
As soon as I run a test, test/fixtures/blog will get created with a users.yml file inside it. It’ll look like this:
chris: name: Chris id: "2" banned: "1" updated_at: 2007-05-09 09:08:04 created_at: 2007-05-09 09:08:04 kevin: name: Kevin id: "3" banned: "0" updated_at: 2007-05-09 09:08:04 created_at: 2007-05-09 09:08:04 tom: name: Tom id: "1" banned: "0" updated_at: 2007-05-09 09:08:04 created_at: 2007-05-09 09:08:04
Notice we’ve got intelligent fixture names. FixtureScenarioBuilder did its best to guess these by trying name, username, then title. If it can’t figure out a name, it falls back to stories_001 or whatever.
As for quick, iterative, agile development: whenever you modify scenarios.rb, your YAML fixtures will get re-built. Any errors in your scenarios.rb file will be clearly surfaced when building.
FixtureScenarioBuilder also supports nesting fixtures:
scenario :blog do %w( Tom Chris ).each do |user| User.create! :name => user end end scenario :banned_users => :blog do User.create! :name => 'Kevin', :banned => true end
As you may guess, this creates test/fixtures/blog and test/fixtures/blog/banned_users. Rake-like and tasty.
Ruby fixtures are super because you can easily throw around associations, forget about those updated_at & created_at fields, easily refactor fixtures after making sweeping schema changes, and don’t need to worry about join tables.
I’ve been doing fixtures this way for a few months now and really dig. FixtureScenarios and FixtureScenarioBuilder are like a one-two knock out punch of… uh… test data.
Step 4: Real Life And All
For a more complex example, check out this scenarios.rb file. It uses nesting, multiple scenarios, and all sorts of Ruby goodness (for a code review site, ya heard).
You can get a lot of this stuff by ERBing your YAML files, but really, that sucks. I like having my habtm join tables generated for me, I am comfortable defining associations and whatnot in Ruby. It’s how I think.
These days, many people are hacking their database or mocking to get around the fixture pain. Other fixture solutions are the fixture references plugin, this patch, and QuickFix. I’m sure there are countless others. Got a good solution? Leave a comment, plz.
Step 5: Get Out of Here
Thanks, and goodnight.