SlideShare a Scribd company logo
Philly.rb meetup 
Rails testing: 
factories or 
fixtures? 
Michael Toppa 
March 11, 2014 
@mtoppa
Why use factories or fixtures? 
❖ Factories and fixtures can simplify otherwise repetitive and 
complex data setup for tests 
❖ Your need for them in unit tests will be light if you do TDD with 
loosely-coupled code 
❖ But they are vital for unit-“ish” testing if you’re working with 
tightly coupled code 
❖ e.g. most Rails apps, and the examples in this presentation 
❖ They are great for integration testing
Comparisons 
❖ Tests with no factories or fixtures 
❖ Tests with fixtures 
❖ Tests with factories, using FactoryGirl 
❖ We’ll test the same method in each case, so you can 
clearly see the differences
Partial data model for our 
examples 
Candidate 
(politician) 
Campaign Race 
Office 
Current 
Holders 
Winning Campaign
Our test case
Tests with no factories or 
fixtures
Ok for simple cases 
# spec/models/candidate_spec.rb 
describe Candidate do 
describe "#calculate_completeness" do 
it "returns 0 when no fields are filled out" do 
toppa = Candidate.new 
toppa.calculate_completeness.should eq(0.0) 
end 
it "returns a ratio of filled-out fields to total fields" do 
toppa = Candidate.new 
toppa.name = "Mike Toppa" 
toppa.facebook_url = "https://facebook/ElJefe" 
toppa.wikipedia_url = "http://en.wikipedia.org/wiki/Mike_Toppa" 
toppa.calculate_completeness.should eq(0.2) # 3 / 15 = 0.2 
end 
end
Not so good for complex 
cases # spec/models/race_spec.rb 
describe Race do 
describe '#inaugurate!' do 
it 'makes Mike Toppa President of the United States' do 
toppa = Candidate.create( 
name: 'Mike Toppa', 
[and all required attributes] 
) 
president = Office.create( 
title: 'President of the United States', 
[and all required attributes] 
) 
presidential_race = Race.create([president + all req attrs]) 
toppa_campaign = Campaign.create([toppa + presidential_race + all 
req attrs]) 
presidential_race.winning_campaign = toppa_campaign 
presidential_race.inaugurate! 
president.current_holders.first.should == toppa 
end 
end 
end
Tests with fixtures
The fixture files 
# spec/fixtures/candidates.yml 
mike_toppa: 
id: 1 
name: Mike Toppa 
gender: M 
[etc…] 
# spec/fixtures/office.yml 
president: 
id: 1 
title: President of the United States 
level: N 
[etc…] 
# spec/fixtures/race.yml 
president_race_2012: 
id: 1 
office: president 
election_day: 10/4/2012 
[etc…] 
# spec/fixtures/campaign.yml 
toppa_us_president_campaign_2012: 
id: 1 
race: president_race_2012 
candidate: mike_toppa 
fec_id: XYZ 
[etc…]
The test file 
# spec/models/race_spec.rb 
describe Race do 
describe '#inaugurate!' do 
it 'makes Mike Toppa President of the United States' do 
toppa = candidates(:mike_toppa) 
president = offices(:president) 
presidential_race = races(:us_president_race_2012) 
toppa_campaign = campaigns(:toppa_us_president_campaign_2012) 
presidential_race.winning_campaign = toppa_campaign 
presidential_race.inaugurate! 
president.current_holders.first.should == toppa 
end 
end 
end
Pros 
❖ For simple scenarios, re-usable across tests 
❖ Easy to generate from live data 
❖ Fixture files are easy to read 
❖ Tests are fairly fast 
❖ Records are inserted based on the fixture files, 
bypassing ActiveRecord
Cons 
❖ Brittle 
❖ If you add required fields to a model, you have to update 
all its fixtures 
❖ Fixture files are an external dependency 
❖ Fixtures are organized by model - you need to keep track 
of their relationships with test scenarios 
❖ Not dynamic - you get the same values every time 
❖ (this may or may not be ok)
Tests with factories, 
using FactoryGirl
The factory files 
# spec/factories/candidates.rb 
FactoryGirl.define do 
factory :candidate do 
name 'Jane Doe' 
gender 'F' 
[etc…] 
# spec/factories/offices.rb 
FactoryGirl.define do 
factory :office do 
title 'Senator' 
level 'N' 
[etc…] 
# spec/factories/races.rb 
FactoryGirl.define do 
factory :race do 
office 
election_day '11/04/2014'.to_datetime 
[etc…] 
# spec/factories/campaigns.rb 
FactoryGirl.define do 
factory :campaign do 
candidate 
race 
fec_id 'XYZ' 
[etc…]
The test file 
# spec/models/race_spec.rb 
describe Race do 
describe '#inaugurate!' do 
it 'makes Mike Toppa President of the United States' do 
toppa = create :candidate, name: 'Mike Toppa' 
president = create( 
:office, 
title: 'President of the United States' 
) 
presidential_race = create :race, office: president 
toppa_campaign = create( 
:campaign, 
candidate: toppa, 
race: presidential_race 
) 
presidential_race.winning_campaign = toppa_campaign 
presidential_race.inaugurate! 
president.current_holders.first.should == toppa 
end 
end 
end
But wait, there’s more…
Randomized values with the 
Faker gem 
# spec/factories/candidates.rb 
FactoryGirl.define do 
factory :candidate do 
name { Faker::Name.name } 
wikipedia_url { Faker::Internet.url } 
short_bio { Faker::Lorem.paragraph } 
phone { Faker::PhoneNumber.phone_number} 
address_1 { Faker::Address.street_address } 
address_2 { Faker::Address.secondary_address } 
city { Faker::Address.city } 
state { Faker::Address.state } 
zip { Faker::Address.zip_code } 
# and other kinds of randomized values 
gender { |n| %w[M F].sample } 
sequence(:pvs_id) 
azavea_updated_at { Time.at(rand * Time.now.to_i) } 
[etc…]
Debate on randomized values 
❖ Argument for: 
❖ Having a wide variety of values, and combinations of values, 
in your tests can expose bugs you might otherwise miss 
❖ Argument against: 
❖ In a test using many different model instances, failures can be 
difficult to reproduce and debug 
❖ If you’re counting on randomized values to find bugs, your 
design process may not be robust
Instantiation options 
❖ create: saves your object to the database, and saves 
any associated objects to the database 
❖ build: builds your object in memory only, but still saves 
any associated objects to the database 
❖ build_stubbed: builds your object in memory only, as 
well as any associated objects
Instantiation options 
❖ Use build_stubbed whenever possible - your tests 
will be faster! 
❖ You will need to use create or build for integration 
testing 
❖ …and if you’re stuck with tightly coupled Rails code
For frequent scenarios: child 
factories 
# spec/factories/offices.rb 
FactoryGirl.define do 
factory :office do 
title 'Mayor' 
level 'L' 
[etc…] 
factory :office_president do 
status 'A' 
title 'President of the United States' 
level 'N' 
[etc…] 
end 
end 
end 
# spec/models/race_spec.rb 
describe Race do 
describe '#inaugurate!' do 
it 'makes Mike Toppa President of the United States' do 
president = create :office_president 
[etc…] 
end 
end 
end
Child factories using other child 
factories 
# spec/factories/offices.rb 
FactoryGirl.define do 
factory :office do 
area 
title 'Mayor' 
level 'L' 
[etc…] 
factory :office_house do 
association :area, factory: :congressional_district 
status 'A' 
level 'N' 
type_code 'H' 
[etc…] 
end 
end 
end
For frequent scenarios: traits 
# spec/factories/candidates.rb 
FactoryGirl.define do 
factory :candidate do 
name 'Jane Doe' 
gender 'F' 
[etc…] 
end 
trait :with_office_house do 
after :create do |candidate| 
office = create :office_house 
create :current_office_holder, :office => office, :candidate => candidate 
end 
end 
end 
# spec/models/race_spec.rb 
describe Race do 
describe '#inaugurate!' do 
it 'makes Mike Toppa President of the United States' do 
toppa = create: candidate, :with_office_house 
[etc…] 
end 
end 
end
Don’t overuse child factories 
and traits - leads to brittleness
Factories address shortcomings of 
fixtures 
❖ Not as brittle 
❖ Factory won’t break if you add a new required field to a model 
❖ You don’t need to maintain complex scenarios spread out across 
fixture files 
❖ Lessened external dependency, more flexibility 
❖ You can define the attributes important to the test in the test code 
itself 
❖ When using build_stubbed your tests will be faster 
❖ But fixtures are faster when inserting records

More Related Content

PDF
The Many Faces of Swift Functions
PDF
PDF
Yeosu
DOCX
Paula montenegro
PDF
Pk chemicals
PPTX
PDF
Libraries creating reading material for rural children readers in Burkina Faso
PPTX
FAVL and Rotary funded multimedia center of Houndé
The Many Faces of Swift Functions
Yeosu
Paula montenegro
Pk chemicals
Libraries creating reading material for rural children readers in Burkina Faso
FAVL and Rotary funded multimedia center of Houndé

Similar to fixtures_vs_AR_vs_factories.ppt (20)

PPT
Rails testing: factories or fixtures?
PDF
2011-02-03 LA RubyConf Rails3 TDD Workshop
PPTX
Factory girl
PDF
Tootle returning to rails
PDF
Funtional Ruby - Mikhail Bortnyk
PDF
Functional Ruby
PPT
Ruby on Rails testing with Rspec
PPT
TDD, BDD, RSpec
PDF
RSpec on Rails Tutorial
PDF
TI1220 Lecture 8: Traits & Type Parameterization
ODP
Fixture Replacement Plugins
PDF
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
PDF
Fixtures and Factories with python-factoryboyfactoryboy_hectorcanto.pdf
PDF
Ruby on rails rspec
PPTX
Factory in python
PDF
Introduction to unit testing
PDF
Factory Girl
KEY
Testing gone-right
PDF
Testing con spock
Rails testing: factories or fixtures?
2011-02-03 LA RubyConf Rails3 TDD Workshop
Factory girl
Tootle returning to rails
Funtional Ruby - Mikhail Bortnyk
Functional Ruby
Ruby on Rails testing with Rspec
TDD, BDD, RSpec
RSpec on Rails Tutorial
TI1220 Lecture 8: Traits & Type Parameterization
Fixture Replacement Plugins
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
Fixtures and Factories with python-factoryboyfactoryboy_hectorcanto.pdf
Ruby on rails rspec
Factory in python
Introduction to unit testing
Factory Girl
Testing gone-right
Testing con spock
Ad

More from Tom Plisskin (20)

PPT
practicepowerpoint.ppt
PPT
test presentation
PPT
test presentation
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
cxvxcvcxv
PPT
PPT
PPT
PPT
PPT
PPT
PPT
dfgdfgvfd
PPT
dfgdfg
PPT
dfgdfg
PPT
dfgdfg
practicepowerpoint.ppt
test presentation
test presentation
cxvxcvcxv
cxvxcvcxv
cxvxcvcxv
cxvxcvcxv
cxvxcvcxv
cxvxcvcxv
cxvxcvcxv
dfgdfgvfd
dfgdfg
dfgdfg
dfgdfg
Ad

fixtures_vs_AR_vs_factories.ppt

  • 1. Philly.rb meetup Rails testing: factories or fixtures? Michael Toppa March 11, 2014 @mtoppa
  • 2. Why use factories or fixtures? ❖ Factories and fixtures can simplify otherwise repetitive and complex data setup for tests ❖ Your need for them in unit tests will be light if you do TDD with loosely-coupled code ❖ But they are vital for unit-“ish” testing if you’re working with tightly coupled code ❖ e.g. most Rails apps, and the examples in this presentation ❖ They are great for integration testing
  • 3. Comparisons ❖ Tests with no factories or fixtures ❖ Tests with fixtures ❖ Tests with factories, using FactoryGirl ❖ We’ll test the same method in each case, so you can clearly see the differences
  • 4. Partial data model for our examples Candidate (politician) Campaign Race Office Current Holders Winning Campaign
  • 6. Tests with no factories or fixtures
  • 7. Ok for simple cases # spec/models/candidate_spec.rb describe Candidate do describe "#calculate_completeness" do it "returns 0 when no fields are filled out" do toppa = Candidate.new toppa.calculate_completeness.should eq(0.0) end it "returns a ratio of filled-out fields to total fields" do toppa = Candidate.new toppa.name = "Mike Toppa" toppa.facebook_url = "https://facebook/ElJefe" toppa.wikipedia_url = "http://en.wikipedia.org/wiki/Mike_Toppa" toppa.calculate_completeness.should eq(0.2) # 3 / 15 = 0.2 end end
  • 8. Not so good for complex cases # spec/models/race_spec.rb describe Race do describe '#inaugurate!' do it 'makes Mike Toppa President of the United States' do toppa = Candidate.create( name: 'Mike Toppa', [and all required attributes] ) president = Office.create( title: 'President of the United States', [and all required attributes] ) presidential_race = Race.create([president + all req attrs]) toppa_campaign = Campaign.create([toppa + presidential_race + all req attrs]) presidential_race.winning_campaign = toppa_campaign presidential_race.inaugurate! president.current_holders.first.should == toppa end end end
  • 10. The fixture files # spec/fixtures/candidates.yml mike_toppa: id: 1 name: Mike Toppa gender: M [etc…] # spec/fixtures/office.yml president: id: 1 title: President of the United States level: N [etc…] # spec/fixtures/race.yml president_race_2012: id: 1 office: president election_day: 10/4/2012 [etc…] # spec/fixtures/campaign.yml toppa_us_president_campaign_2012: id: 1 race: president_race_2012 candidate: mike_toppa fec_id: XYZ [etc…]
  • 11. The test file # spec/models/race_spec.rb describe Race do describe '#inaugurate!' do it 'makes Mike Toppa President of the United States' do toppa = candidates(:mike_toppa) president = offices(:president) presidential_race = races(:us_president_race_2012) toppa_campaign = campaigns(:toppa_us_president_campaign_2012) presidential_race.winning_campaign = toppa_campaign presidential_race.inaugurate! president.current_holders.first.should == toppa end end end
  • 12. Pros ❖ For simple scenarios, re-usable across tests ❖ Easy to generate from live data ❖ Fixture files are easy to read ❖ Tests are fairly fast ❖ Records are inserted based on the fixture files, bypassing ActiveRecord
  • 13. Cons ❖ Brittle ❖ If you add required fields to a model, you have to update all its fixtures ❖ Fixture files are an external dependency ❖ Fixtures are organized by model - you need to keep track of their relationships with test scenarios ❖ Not dynamic - you get the same values every time ❖ (this may or may not be ok)
  • 14. Tests with factories, using FactoryGirl
  • 15. The factory files # spec/factories/candidates.rb FactoryGirl.define do factory :candidate do name 'Jane Doe' gender 'F' [etc…] # spec/factories/offices.rb FactoryGirl.define do factory :office do title 'Senator' level 'N' [etc…] # spec/factories/races.rb FactoryGirl.define do factory :race do office election_day '11/04/2014'.to_datetime [etc…] # spec/factories/campaigns.rb FactoryGirl.define do factory :campaign do candidate race fec_id 'XYZ' [etc…]
  • 16. The test file # spec/models/race_spec.rb describe Race do describe '#inaugurate!' do it 'makes Mike Toppa President of the United States' do toppa = create :candidate, name: 'Mike Toppa' president = create( :office, title: 'President of the United States' ) presidential_race = create :race, office: president toppa_campaign = create( :campaign, candidate: toppa, race: presidential_race ) presidential_race.winning_campaign = toppa_campaign presidential_race.inaugurate! president.current_holders.first.should == toppa end end end
  • 18. Randomized values with the Faker gem # spec/factories/candidates.rb FactoryGirl.define do factory :candidate do name { Faker::Name.name } wikipedia_url { Faker::Internet.url } short_bio { Faker::Lorem.paragraph } phone { Faker::PhoneNumber.phone_number} address_1 { Faker::Address.street_address } address_2 { Faker::Address.secondary_address } city { Faker::Address.city } state { Faker::Address.state } zip { Faker::Address.zip_code } # and other kinds of randomized values gender { |n| %w[M F].sample } sequence(:pvs_id) azavea_updated_at { Time.at(rand * Time.now.to_i) } [etc…]
  • 19. Debate on randomized values ❖ Argument for: ❖ Having a wide variety of values, and combinations of values, in your tests can expose bugs you might otherwise miss ❖ Argument against: ❖ In a test using many different model instances, failures can be difficult to reproduce and debug ❖ If you’re counting on randomized values to find bugs, your design process may not be robust
  • 20. Instantiation options ❖ create: saves your object to the database, and saves any associated objects to the database ❖ build: builds your object in memory only, but still saves any associated objects to the database ❖ build_stubbed: builds your object in memory only, as well as any associated objects
  • 21. Instantiation options ❖ Use build_stubbed whenever possible - your tests will be faster! ❖ You will need to use create or build for integration testing ❖ …and if you’re stuck with tightly coupled Rails code
  • 22. For frequent scenarios: child factories # spec/factories/offices.rb FactoryGirl.define do factory :office do title 'Mayor' level 'L' [etc…] factory :office_president do status 'A' title 'President of the United States' level 'N' [etc…] end end end # spec/models/race_spec.rb describe Race do describe '#inaugurate!' do it 'makes Mike Toppa President of the United States' do president = create :office_president [etc…] end end end
  • 23. Child factories using other child factories # spec/factories/offices.rb FactoryGirl.define do factory :office do area title 'Mayor' level 'L' [etc…] factory :office_house do association :area, factory: :congressional_district status 'A' level 'N' type_code 'H' [etc…] end end end
  • 24. For frequent scenarios: traits # spec/factories/candidates.rb FactoryGirl.define do factory :candidate do name 'Jane Doe' gender 'F' [etc…] end trait :with_office_house do after :create do |candidate| office = create :office_house create :current_office_holder, :office => office, :candidate => candidate end end end # spec/models/race_spec.rb describe Race do describe '#inaugurate!' do it 'makes Mike Toppa President of the United States' do toppa = create: candidate, :with_office_house [etc…] end end end
  • 25. Don’t overuse child factories and traits - leads to brittleness
  • 26. Factories address shortcomings of fixtures ❖ Not as brittle ❖ Factory won’t break if you add a new required field to a model ❖ You don’t need to maintain complex scenarios spread out across fixture files ❖ Lessened external dependency, more flexibility ❖ You can define the attributes important to the test in the test code itself ❖ When using build_stubbed your tests will be faster ❖ But fixtures are faster when inserting records

Editor's Notes

  • #5: Introductions to using tools usually have overly-simple examples. We’re going to use more realistic examples, as you can only get a feel for the differences between factories and fixtures when dealing with more complex scenarios.
  • #6: The method we’ll examine is for inaugurating a new office holder, and we’ll make me President of the United States
  • #8: Fast, simple, clear (not hitting the database)
  • #9: Specifying all the attributes is cumbersome and when reading the test, distracts from understanding the purpose of the test Brittle: if add a required field to a model, you have to update all the tests using that model Hard to reuse
  • #11: For each model’s fixture file, you can have multiple records defined updated_at and created_at are set automatically with the current time, or you can specify them
  • #16: A simple factory file can look a lot like a fixture file…
  • #17: … but a key difference is that you can override values and specify associations when writing the test