factory girl
DESCRIPTION
A not so brief overview of Factory Girl's features and basic usage.TRANSCRIPT
Factory GirlA Brief Introduction Using Ruby on Rails
Factory GirlA Brief Introduction Using Ruby on Rails
Yet Another Blog Example
What’s Factory Girl?
A Replacement for Fixtures
Provides a Simple DSL
Keeps TestsFocused & Readable
Builds Objects Instead of Database Records
Installation
Gemfile
group :development, :test do gem ‘factory_girl_rails’end
Gemfile
$ bundle install
Defining a Factory
spec/factories/
spec/factories/users.rb
spec/factories/posts.rb
spec/factories/comments.rb
spec/factories/products.rb
spec/factories/users.rb
FactoryGirl.define do factory :user do name ‘Joe Blow’ email ‘[email protected]’ password ‘p@ssw0rd’ admin false endend
spec/factories/users.rb
spec/models/user_spec.rb
spec/models/user_spec.rb
describe User do
end
spec/models/user_spec.rb
describe User do subject { FactoryGirl.create(:user) }
end
spec/models/user_spec.rb
describe User do subject { FactoryGirl.create(:user) }
its(:name) { should == ‘Joe Blow’ }
end
spec/models/user_spec.rb
describe User do subject { FactoryGirl.create(:user) }
its(:name) { should == ‘Joe Blow’ } its(:email) { should == ‘[email protected]’ }
end
describe User do subject { FactoryGirl.create(:user) }
its(:name) { should == ‘Joe Blow’ } its(:email) { should == ‘[email protected]’ }
it { should_not be_admin }end
spec/models/user_spec.rb
FactoryGirl.create(:user)
FactoryGirl.build(:user)
FactoryGirl.build_stubbed(:user)
spec/spec_helper.rb
RSpec.configure do |config| config.include FactoryGirl::Syntax::Methodsend
spec/spec_helper.rb
FactoryGirl.create(:user)
create(:user)
build(:user)
build_stubbed(:user)
spec/models/user_spec.rb
describe User do subject { create(:user) }
its(:name) { should == ‘Joe Blow’ } its(:email) { should == ‘[email protected]’ }
it { should_not be_admin }end
Overriding Attributes
Keep Only Relevant Attributes in Tests
spec/models/user_spec.rb
context ‘a duplicate email address’ do it { should_not be_valid }end
context ‘a duplicate email address’ do let(:first_user) { User.create(name: ‘Joe First’, email: ‘[email protected]’, password: ‘test123’, admin: false) } subject { User.new(name: ‘Joe Duplicate’, email: first_user.email, password: ‘test123’, admin: false) }
it { should_not be_valid }end
spec/models/user_spec.rb
context ‘a duplicate email address’ do let(:user) { create(:user) } subject { build(:user, email: user.email) }
it { should_not be_valid }end
spec/models/user_spec.rb
Lazy Attributes
Lazy Attributes Are Dynamic Attributes
spec/factories/posts.rb
FactoryGirl.define do factory :post do title ‘This is my post.’ content ‘There are many like it, but this one is mine.’ published_at Time.now endend
irb(main):001:0> FactoryGirl.create(:post)
irb(main):001:0> FactoryGirl.create(:post)=> #<Post id: 1, title: “This is my post.”, content: “There are many like it, but this one is mine.”, published_at: 2012-12-20 23:47:34 UTC>
group :development, :test do gem ‘factory_girl_rails’ gem ‘ffaker’end
Gemfile
spec/factories/posts.rb
FactoryGirl.define do factory :post do title { Faker::Lorem.sentence } content { Faker::Lorem.sentences.join(‘ ’) } published_at { Time.now } endend
irb(main):001:0> FactoryGirl.create(:post)
irb(main):001:0> FactoryGirl.create(:post)=> #<Post id: 2, title: “Lorem ipsum dolor sit amet, consectetur adipiscing elit.”, content: “Pellentesque adipiscing varius suscipit …”, published_at: 2012-12-20 23:48:14 UTC>
irb(main):001:0> FactoryGirl.create(:post)=> #<Post id: 3, title: “Nulla id augue ut mauris pharetra tincidunt ac sed velit.”, content: “Nullam tincidunt, dolor quis pellentesque …”, published_at: 2012-12-20 23:49:06 UTC>
Sequences
Reuse Common Attributes
FactoryGirl.define do sequence :email do |n| “person#{n}@example.com” endend
spec/factories/sequences.rb
FactoryGirl.define do factory :user do name { Faker::Name.name } email password ‘p@ssw0rd’ admin false endend
spec/factories/users.rb
Dependent Attributes
Attributes FromOther Attributes
spec/factories/users.rb
FactoryGirl.define do factory :user do name ‘Joe Blow’ email ‘[email protected]’ password ‘p@ssw0rd’ admin false endend
spec/factories/users.rb
FactoryGirl.define do factory :user do name ‘Joe Blow’ email { ‘#{name.gsub(/\s/, ‘.’)}@example.com’.downcase } password ‘p@ssw0rd’ admin false endend
irb(main):001:0> FactoryGirl.create(:user)=> #<User id: 1, name: “Joe Blow”, email: “[email protected]”, password: “p@ssw0rd”, admin: false>
irb(main):001:0> FactoryGirl.create(:user, name: ‘Joe Mama’)=> #<User id: 2, name: “Joe Mama”, email: “[email protected]”, password: “p@ssw0rd”, admin: false>
Traits
Specify Types Of Factories
describe Post do context ‘an unpublished post’ do subject { create(:post, published_at: nil) }
it ‘will never be read’ do # ... end endend
FactoryGirl.define do factory :post do title { Faker::Lorem.sentence } content { Faker::Lorem.sentences.join(‘ ’) } published_at { Time.now } endend
spec/factories/posts.rb
FactoryGirl.define do factory :post do title { Faker::Lorem.sentence } content { Faker::Lorem.sentences.join(‘ ’) } published_at { Time.now } trait :unpublished do published_at nil end endend
spec/factories/posts.rb
describe Post do context ‘an unpublished post’ do subject { create(:post, :unpublished) }
it ‘will never be read’ do # ... end endend
spec/models/post_spec.rb
Associations
Build Associated Objects From Factories
class Post < ActiveRecord::Base belongs_to :userend
app/models/post.rb
irb(main):001:0> FactoryGirl.create(:post)=> #<Post id: …, title: “…”, content: “…”, published_at: …, user: nil>
FactoryGirl.define do factory :post do title { Faker::Lorem.sentence } content { Faker::Lorem.sentences.join(‘ ’) } published_at { Time.now } trait :unpublished do published_at nil end endend
spec/factories/posts.rb
FactoryGirl.define do factory :post do user title { Faker::Lorem.sentence } content { Faker::Lorem.sentences.join(‘ ’) } published_at { Time.now } trait :unpublished do published_at nil end endend
spec/factories/posts.rb
irb(main):001:0> FactoryGirl.create(:post)=> #<Post …, user: #<User id: 123, name: “John Wayne”, …>>
Transient Attributes
Customize Behavior With Fake Attributes
FactoryGirl.define do factory :post do ignore do unapproved false end
title { unapproved ? “ಠ_ಠ” : “This is my post.” } endend
spec/factories/posts.rb
FactoryGirl.define do factory :post do ignore do unapproved false end
title { unapproved ? “ಠ_ಠ” : “This is my post.” } endend
spec/factories/posts.rb
FactoryGirl.define do factory :post do ignore do unapproved false end
title { unapproved ? “ಠ_ಠ” : “This is my post.” } endend
spec/factories/posts.rb
irb(main):001:0> FactoryGirl.create(:post)=> #<Post id: 2, title: “This is my post.”>
irb(main):001:0> FactoryGirl.create(:post, unapproved: true)=> #<Post id: 2, title: “ಠ_ಠ”>
Creating or Building Multiple Objects
Build a Shitload Of Objects
FactoryGirl.create_list(:model, 5)
FactoryGirl.build_list(:model, 5)
FactoryGirl.build_stubbed_list(:model, 5)
irb(main):001:0> FactoryGirl.create_list(:post, 5)
irb(main):001:0> FactoryGirl.create_list(:post, 5)=> [#<Post id: 1, title: “This is my post.”>, #<Post id: 2, title: “This is my post.”>, #<Post id: 3, title: “This is my post.”>, #<Post id: 4, title: “This is my post.”>, #<Post id: 5, title: “This is my post.”>]
Callbacks
Execute Arbitrary Code When Building & Creating
class User < ActiveRecord::Base has_many :postsend
app/models/user.rb
FactoryGirl.define do factory :user_with_posts do ignore do posts_count 5 end
after(:create) do |user, evaluator| FactoryGirl.create_list(:post, evaluator.posts_count, user: user) end endend
spec/factories/users.rb
irb(main):001:0> FactoryGirl.create(:user).posts.length=> 0
irb(main):001:0> FactoryGirl.create(:user_with_posts).posts.length=> 5
irb(main):001:0> FactoryGirl.create(:user_with_posts,posts_count: 15).posts.length=> 15
Wrapping Up...
Factory Girl is Magic
Easier to Maintain
Less of a Headache
Fun to Work With
Questions?