For the longest time, I’ve used fixtures as a means to seed some basic data in my development environment. The goal is to put the application in a useable state without having to manually create data (such as users or anything else required to run the app). In my most recent project, however, I’ve completely abandoned fixtures, and I doubt I’ll ever look back.
I felt two kinds of pain with fixture data – the pain of maintaining it and the lack of quality data.
The pain of maintaining fixtures was usually bad enough (and I was lazy enough) that I typically just ignored it altogether. With every change to the schema, more data had to be added or changed. And not just one place, since a fixture usually contains several records. I just don’t have the patience for it.
Due to my lack of patience, fixture data in any of my projects has been completely unreliable. I’ll leave out fields that end up being required. Or I’ll only put one record in a fixture file because I’m too lazy bother with more.
I put up with this pain for too long. The ability to quickly seed a development database with valid, useable data is a big deal, and I was done ignoring the problem. Enter Faker and Factory Girl.
You probably already know what Factory Girl is, but in case you don’t, it’s just another factory implementation, and it happens to be my preferred implementation at the moment. If you prefer another, I’m sure it will work just as well with what I’m describing here.
Faker is a Ruby library for generating pseudo-random data. Typically, this is exactly the kind of data that I want in my development database. Repeatability isn’t that important. Faker will generate names for people and business, addresses, phone numbers, Lorem text, BS, and more. I use Faker in my factories as a template for pseudo-random models:
Factory.define :user do |u|
u.email { Factory.next :email }
u.email_confirmation { |u| u.email }
u.password 'test'
u.password_confirmation { |u| u.password }
u.first_name 'Test'
u.last_name 'User'
end
Factory.define :user_sample, :parent => :user do |u|
u.first_name { Faker::Name.first_name }
u.last_name { Faker::Name.last_name }
end
Here, the ‘user’ factory is used in tests. It doesn’t use Faker, because I need my tests to be consistent and repeatable. The ‘user_sample’ factory, however, inherits the ‘user’ factory and uses Faker to generate the name.
Once I have my factories ready to go, I create a custom Rake task to seed my development database:
namespace :db do
desc 'Provide a base load of randomly generated (but valid) data for development'
task :seed => [:reset, 'fixtures:load'] do
# generate users
users = []
6.times { users << Factory(:user_sample) }
users << Factory(:user, :email => 'user@edgecase.com', :role => 'user')
users << Factory(:user, :email => 'admin@edgecase.com', :role => 'admin')
# generate orders
20.times { Factory(:order_sample, :user => users.rand) }
# login instructions
puts "\n**************\n\nThe following accounts are available for use:\n\n"
puts ' user@edgecase.com (password: test)'
puts ' admin@edgecase.com (password: test)'
puts "\n**************\n\n"
end
end
With this in place, I can run rake db:seed, and I’ll get a nicely bootstrapped development database to poke around in. Each order will belong to a random user with a random name. It’ll be different every time you run it. It’s like a box of chocolates. Or something.
Want more users? Change 6 to 60. Lovely.
Notice that db:seed still has db:fixtures:load as a prerequisite. This means that fixtures are still an option if I want to combine them with my custom seed task. Nice as that sounds, I haven’t used it. Fixtures are, for me, but a memory.