25 March 2014
Testing with FactoryGirl and Faker
In the context of Rails development, fixtures provide an easy way to create sample data and
make it available to your tests. Every time Rails generates a model, it
also creates a corresponding YAML file in test/fixtures
for your fixture code.
test/fixtures/users.yml
meryl:
name: Meryl Streep
birthday: 1949-06-22
occupation: Actress
You can access fixtures in your tests as follows:
users(:meryl)
While fixtures let you weave elaborate narratives involving Meryl Streep’s interactions with your site, they tend not to be very
DRY. For example, if you want access to 12 pre-defined instances of User
in your specs, you need to define 12 fixures in your
users.yml file. This process can be cumbersome and error-prone.
Factory Girl
There’s a gem for that.
Gemfile
gem 'factory_girl_rails'
Factory Girl is Thoughtbot’s solution to this problem, and it is awesome. Rather than hardcode your fixture data, Factory Girl lets you define factories – methods that dynamically generate objects – using a friendly DSL.
spec/factories.rb
FactoryGirl.define do
factory :user do
name "Meryl Streep"
birthday "1949-06-22"
occupation "Actress"
end
end
It then exposes methods for assembling users from said factories in your tests.
spec/models/users.rb
build(:user)
#=> unpersisted version of Meryl
create(:user)
#=> persisted version of Meryl (separate object)
Sequences
Let’s say your user model validates uniqueness on its ‘name’ attribute, and you need to create 20 valid users in your tests (each with a unique name). Two ‘Meryl Streep’ s will no longer fly.
Luckily, Factory Girl provides a build-in fix in the form of sequences.
factory :user do
sequence(:name) { |n| "Meryl Streep#{n}" }
birthday "1949-06-22"
occupation "Actress"
end
Then…
spec/models/users.rb
create(:user).name
#=> "Meryl Streep1"
create(:user).name
#=> "Meryl Streep2"
create(:user).name
#=> "Meryl Streep3"
Strange example, but you get the picture.
Faker
Given that (possibly) not everyone loves Meryl Streep as much as I do, wouldn’t it be nice if our factory could dynamically generate unique, natural-sounding names for each user it created?
There’s a gem for that.
gemfile
gem 'faker'
spec/factories
factory :user do
name Faker::Name.name
birthday "1949-06-22"
occupation "Actress"
end
spec/users_spec.rb
create(:user).name
#=> "Anastacio Jast"
create(:user).name
#=> "Edna Weimann"
Faker can fake out lots of other things as well:
Faker::Internet.email
#=> "rosanna_smitham@vandervort.biz"
Faker::Commerce.product
#=> "Intelligent Steel Computer"
Faker::Business.credit_card_number
#=> "1212-1221-1121-1234"
…
Meryl Streep1: Yes, that’ll be one Intelligent Steel Computer, please. Do you accept credit
cards?
Meryl Streep2: So, this is the beginning of happiness. This is where it starts.
And, of course, there will always be more.
Meryl Streep3: Alright ladies, that’s a wrap.