Singleton factories in factory_girl/machinist?
Asked Answered
S

3

17

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.

Schoolbook answered 30/11, 2010 at 19:48 Comment(0)
B
25

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

Busy answered 11/7, 2012 at 5:14 Comment(1)
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_initializeMirabella
O
4

@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
Orientalize answered 15/5, 2019 at 8:59 Comment(0)
E
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).

Elwoodelwyn answered 17/2, 2012 at 11:3 Comment(1)
More detailed answer in this topic, including a more thorough explanation of the above, plus an alternative solution if you are using Cucumber: #2015973Elwoodelwyn

© 2022 - 2024 — McMap. All rights reserved.