Fixture replacement plugins Jakub Suder Lunar Logic Polska
Why fixtures aren't enough „Rails test fixtures are evil and a plague upon developers.”   http://b.logi.cx/2007/11/26/object-daddy „If you are still using fixtures, there is no hope for you. Become a forest ranger.” http://blog.adsdevshop.com/2009/02/23/factories-for-test-objects-use-them/
Why fixtures aren't enough not readable – hard to remember which record has what properties
no one remembers the „story” it "should be possible to delete a folder only in a group that you're a member of" do folder = folders(:ruby) folder.should be_deletable_by(users(:joe)) folder.should be_deletable_by(users(:joan)) folder.should_not be_deletable_by(users(:bill)) end
Why fixtures aren't enough information scattered over many files
fixtures can be invalid
some things are much more difficult than they should be
not DRY
slow?
Why fixtures aren't enough it can get complicated... class ScheduleEntryFfsBillingTest < Test::Unit::TestCase fixtures :parties, :locations, :cost_centers, :service_places, :activities, :logins, :tuple_domains, :tuples, :client_payer_relations, :credentials, :gl_mappings, :panels, :panel_members, :panel_payers, :fee_matrices, :fl_matrices, :payer_fee_matrices, :pfl_matrices, :pdcrf_matrices, :care_domains, :care_domains_objectives, :client_domains, :allowed_cost_centers, :authorizations, :positions, :accountabilities, :commissioners, :responsibles, :accountability_positions, :chart_entries, :observations, :gl_mappings, :gl_mapping_types, :tags, :taggings, :form_sets, :form_items, :choices def test_validate_ffs_billing count = ScheduleEntry.ffs_ready_to_bill.size assert_difference BillableItem, :count, 98 do entries = ScheduleEntry.validate_ffs_billing assert entries end assert_equal count-130, ScheduleEntry.ffs_ready_to_bill.size end end ( http://b.logi.cx/2007/11/26/object-daddy )
Solutions object generator? def create_event(options = {}) Event.create({:name => 'Event 1', :location => 'Krakow', :date => Date.today}).merge(options) end too simple, difficult to extend d oesn't handle associations well
doesn't handle uniqueness
Solutions FixtureReplacement
Object Daddy
Factory Girl
Machinist
DM-Sweatshop
FixtureReplacement http://replacefixtures.rubyforge.org/
FixtureReplacement (db/example_data.rb) module FixtureReplacement attributes_for :user do |u| password = String.random u.value  = &quot;a value&quot;, u.other  = &quot;other value&quot;, u.another  = String.random,  # random string 10 characters long u.one_more  = String.random(15), # 15 characters long u.password  = password, u.password_confirmation  = password, u.associated_object = default_bar  # expects attributes_for :bar end end
FixtureReplacement String.random
new_user -> User.new
create_user -> User.create!
default_user -> for use inside model_attributes definitions
new_user(:thing => &quot;overridden&quot;)
john = create_user(:username => &quot;john&quot;)
circumvents attr_protected by assigning manually
Object Daddy http://github.com/flogic/object_daddy/
Object Daddy class User < ActiveRecord::Base generator_for :email, :start => 'test@domain.com' do |prev| user, domain = prev.split('@') user.succ + '@' + domain end generator_for :username, :method => :next_user generator_for :name, :start => 'Joe' generator_for(:start_time) { Time.now } generator_for :name, 'Joe' generator_for :age => 25 def self.next_user @last_username ||= 'testuser' @last_username.succ end end
Object Daddy class User < ActiveRecord::Base generator_for :ssn, :class => SSNGenerator  end class SSNGenerator def self.next @last ||= '000-00-0000' @last = (&quot;%09d&quot; % (@last.gsub('-', '').to_i + 1)).sub(/^(\d{3})(\d{2})(\d{4})$/, '\1-\2-\3') end end
Object Daddy it &quot;should have a comment for every forum the user posts to&quot; do @user = User.generate @post = Post.generate @post.comments << Comment.generate(:title => 'first post!!11') @user.should have(1).comments end admin_user = User.generate! do |user| user.activate! user.add_role(&quot;admin&quot;) end User.spawn  # User.new
Object Daddy /spec/exemplars/user_exemplar.rb
automatically creates objects for required relationships
relationships are always saved, regardless if you use spawn or generate
Factory Girl http://blog.adsdevshop.com/2009/02/23/factories-for-test-objects-use-them/ http://giantrobots.thoughtbot.com/2008/6/6/waiting-for-a-factory-girl http://www.thoughtbot.com/projects/factory_girl http://dev.thoughtbot.com/factory_girl/ http://github.com/thoughtbot/factory_girl/
Factory Girl Factory.define :user do |f| f.first_name 'John' f.last_name  'Smith' f.admin  false f.email  { Factory.next(:email) } f.email { |u| &quot;#{u.first_name}.#{u.last_name}@example.com&quot; } end Factory.sequence :email do |n| &quot;somebody#{n}@example.com&quot; end f.sequence(:email) { |n| &quot;person#{n}@example.com&quot; }

Fixture Replacement Plugins