16

Is there some configuration in a factory of factory girl/machinist that forces it to create objects with the same factory name just once during test case and return the same instance all the time? I know, i can do something like:

def singleton name
    @@singletons ||= {}
    @@singletons[name] ||= Factory name
end
...
Factory.define :my_model do |m|
   m.singleton_model { singleton :singleton_model }
end

but maybe there is a better way.

Alexey
  • 9,197
  • 5
  • 64
  • 76

3 Answers3

24

You can use the initialize_with macro inside your factory and check to see if the object already exists, then don't create it over again. This also works when said factory is referenced by associations:

FactoryGirl.define do
  factory :league, :aliases => [:euro_cup] do
    id 1
    name "European Championship"
    owner "UEFA"
    initialize_with { League.find_or_create_by_id(id)}
  end
end

There is a similar question here with more alternatives: Using factory_girl in Rails with associations that have unique constraints. Getting duplicate errors

Community
  • 1
  • 1
CubaLibre
  • 1,675
  • 13
  • 22
  • 2
    With Rails 4, you'll want to use League.where(:id => id).first_or_create -- If you have a before_save that requires information to be set, then you may want find_or_initialize_by_id or first_or_initialize – Professor Todd Feb 15 '13 at 00:43
3

@CubaLibre answer with version 5 of FactoryBot:

FactoryGirl.define do
  factory :league do
    initialize_with { League.find_or_initialize_by(id: id) }
    sequence(:id)
    name "European Championship"
  end
end
cyrilchampier
  • 2,207
  • 1
  • 25
  • 39
1

Not sure if this could be useful to you.

With this setup you can create n products using the factory 'singleton_product'. All those products will have same platform (i.e. platform 'FooBar').

factory :platform do
  name 'Test Platform'
end

factory :product do
  name 'Test Product'
  platform

  trait :singleton do
    platform{
      search = Platform.find_by_name('FooBar')
      if search.blank?
        FactoryGirl.create(:platform, :name => 'FooBar')
      else
        search
      end
    }
  end

  factory :singleton_product, :traits => [:singleton]
end

You can still use the standard product factory 'product' to create a product with platform 'Test Platform', but it will fail when you call it to create the 2nd product (if platform name is set to be unique).

Jonas Bang Christensen
  • 1,041
  • 2
  • 10
  • 18
  • More detailed answer in this topic, including a more thorough explanation of the above, plus an alternative solution if you are using Cucumber: http://stackoverflow.com/questions/2015473/using-factory-girl-in-rails-with-associations-that-have-unique-constraints-gett/8343150#8343150 – Jonas Bang Christensen Feb 20 '12 at 08:14