Fixtures Are Painful
I just got back from RailsConf 2007, it was a brain-expanding conference. I spent a lot of time talking to Rails coders about how they do testing and use fixtures. I saw some patterns emerge. Generally, people agree that fixtures are painful:
- referencing ids in the fixtures is annoying and prone to error
- the lack of a grouping mechanism makes selecting them harder
- fixtures get out of date with model changes
I plan to do a few posts about fixtures in the next coming weeks, to share some of what I have learned. For now, I’ll focus on validation.
Fixtures Break
The project I’m currently working on has a large number of fixtures (over 100 models). It’s become really hard to manage them all. Over time, some fixtures busted, and it became hard to diagnose random problems in tests.
We are fortunate to be working with zenspider, he’s helping us get our tests, dev process and performance in better shape. I complained a lot about invalid fixtures, and how I longed for a Rake task that could identify the broken ones. He offered up this:
namespace :db do
namespace :fixtures do
task :validate => :environment do
name_map = Hash.new { |h,k| h[k] = k }
Dir.chdir("app/models") do
map = `grep set_table_name *.rb`.gsub(/[:\'\"]+|set_table_name/, '').split
Hash[*map].each do |file, name|
name_map[name] = file.sub(/\.rb$/, '')
end
end
Dir["test/fixtures/*.yml"].each do |fixture|
fixture = name_map[File.basename(fixture, ".yml")]
begin
klass = fixture.classify.constantize
klass.find(:all).each do |thing|
unless thing.valid? then
puts "#{fixture}: id ##{thing.id} is invalid:"
thing.errors.full_messages.each do |msg|
puts " - #{msg}"
end
end
end
rescue => e
puts "#{fixture}: skipping: #{e.message}"
end
end
end # validate
end # fixtures
end # db
Put it in your project Rakefile, and you can then run:
$ rake db:fixtures:validate
You will get back a list of all the fixtures that are not valid, with the validation messages.
But what about edge cases? What about “bad” form data? DHH has declared that most folks want all fixtures loaded at the start of the tests. The data in fixtures does not have to pass model validation before it is loaded into the db.
After discussing this with a number of folks, I’m starting to believe:
- All fixtures should be valid at all times
- Fixtures should provide stuff required by your app to run tests (your admin user, default options, etc)
- Edge cases should be created in tests, not fixtures
- Test invalid data by loading a good fixture and changing it
Rake db:fixtures:validate is helpful for keeping them valid. I plan to use it whenever I do a migration, just to ensure that nothing got broken.




I agree with all your bullet points, and that is the approach I take when I’m writing my fixtures. As far as I’m concerned fixtures are just a known database state, and in the “real world” you’d never let your database get into an inconsistent state, so why would you want your fixture data to be invalid?
My next rake task is going to migrate up from scratch, load fixtures, and THEN run this. Something to do before every checkin.
In the mean time, I also ran across this:
http://blog.hasmanythrough.com/2006/8/27/validate-all-your-records
Josh Susser had the same idea last year. Why is this not a standard Rake task?