2011-08-05 12 views
69

¿Alguien puede decirme si me estoy ocupando de la configuración de la manera incorrecta?Cómo configurar una fábrica en FactoryGirl con la asociación has_many

que tienen los siguientes modelos que tienen asociaciones has_many.through:

class Listing < ActiveRecord::Base 
    attr_accessible ... 

    has_many :listing_features 
    has_many :features, :through => :listing_features 

    validates_presence_of ... 
    ... 
end 


class Feature < ActiveRecord::Base 
    attr_accessible ... 

    validates_presence_of ... 
    validates_uniqueness_of ... 

    has_many :listing_features 
    has_many :listings, :through => :listing_features 
end 


class ListingFeature < ActiveRecord::Base 
    attr_accessible :feature_id, :listing_id 

    belongs_to :feature 
    belongs_to :listing 
end 

que estoy usando Rails 3.1.rc4, factorygirl 2.0.2, 1.1.0, y factory_girl_rails rspec. Aquí está mi comprobación básica rspec rspec cordura para la fábrica :listing:

it "creates a valid listing from factory" do 
    Factory(:listing).should be_valid 
end 

aquí es de fábrica (: lista)

FactoryGirl.define do 
    factory :listing do 
    headline 'headline' 
    home_desc 'this is the home description' 
    association :user, :factory => :user 
    association :layout, :factory => :layout 
    association :features, :factory => :feature 
    end 
end 

Los :listing_feature y :feature fábricas son igualmente configuración.
Si la línea association :features está comentada, todas mis pruebas pasan.
Cuando es

association :features, :factory => :feature 

el mensaje de error es undefined method 'each' for #<Feature> que me pareció tenía sentido para mí, porque ya listing.features devuelve una matriz. Así que lo cambié a

association :features, [:factory => :feature] 

y el error que consigo ahora está ArgumentError: Not registered: features se trata sólo de no ser sensibles a la generación de objetos de fábrica de esta manera, o lo que me estoy perdiendo? Muchas gracias por cualquier entrada!

Respuesta

50

La creación de este tipo de asociaciones requiere el uso de las devoluciones de llamada de FactoryGirl.

Aquí encontrará un conjunto perfecto de ejemplos.

http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl

Para llevarlo a casa con tu ejemplo.

Factory.define :listing_with_features, :parent => :listing do |listing| 
    listing.after_create { |l| Factory(:feature, :listing => l) } 
    #or some for loop to generate X features 
end 
+0

acabaste usando asociación: características, [: Fábrica =>: Función]? – davidtingsu

81

Como alternativa, puede usar un bloque y omitir la palabra clave association. Esto permite crear objetos sin guardar en la base de datos (de lo contrario, una asociación has_many guardará sus registros en el archivo db, incluso si usa la función build en lugar de create).

FactoryGirl.define do 
    factory :listing_with_features, :parent => :listing do |listing| 
    features { build_list :feature, 3 } 
    end 
end 
+4

Este es el maullido del gato. La capacidad de 'build' y' create' lo convierte en el patrón más versátil. A continuación, utilice esta estrategia de compilación de FG personalizada https://gist.github.com/Bartuz/74ee5834a36803d712b7 para 'publicar atributos_interados_para' al probar acciones de controlador que 'accept_nested_attributes_for' –

+3

upvoted, mucho más legible y versátil que la respuesta aceptada IMO –

18

probé unos enfoques diferentes y este es el que trabajó más fiable para mí (adaptado a su caso)

FactoryGirl.define do 
    factory :user do 
    # some details 
    end 

    factory :layout do 
    # some details 
    end 

    factory :feature do 
    # some details 
    end 

    factory :listing do 
    headline 'headline' 
    home_desc 'this is the home description' 
    association :user, factory: :user 
    association :layout, factory: :layout 
    after(:create) do |liztng| 
     FactoryGirl.create_list(:feature, 1, listing: liztng) 
    end 
    end 
end 
0

Así es como me puse el mío:

# Model 1 PreferenceSet 
class PreferenceSet < ActiveRecord::Base 
    belongs_to :user 
    has_many :preferences, dependent: :destroy 
end 

#Model 2 Preference 

class Preference < ActiveRecord::Base  
    belongs_to :preference_set 
end 



# factories/preference_set.rb 

FactoryGirl.define do 
    factory :preference_set do 
    user factory: :user 
    filter_name "market, filter_structure" 

    factory :preference_set_with_preferences do 
     after(:create) do |preference| 
     create(:preference, preference_set: preference) 
     create(:filter_structure_preference, preference_set: preference) 
     end 
    end 
    end 

end 

# factories/preference.rb 

FactoryGirl.define do 
    factory :preference do |p| 
    filter_name "market" 
    filter_value "12" 
    end 

    factory :filter_structure_preference, parent: :preference do 
    filter_name "structure" 
    filter_value "7" 
    end 
end 

Y luego en sus pruebas puede hacer:

@preference_set = FactoryGirl.create(:preference_set_with_preferences) 

Espero que ayude.

11

Usted podría utilizar trait:

FactoryGirl.define do 
    factory :listing do 
    ... 

    trait :with_features do 
     features { build_list :feature, 3 } 
    end 
    end 
end 

Con callback, si necesita la creación DB:

... 

trait :with_features do 
    after(:create) do |listing| 
    create_list(:feature, 3, listing: listing) 
    end 
end 

uso en sus especificaciones de esta manera:

let(:listing) { create(:listing, :with_features) } 

Esto eliminará la duplicación en sus fábricas y ser más reutilizable.

https://robots.thoughtbot.com/remove-duplication-with-factorygirls-traits

Cuestiones relacionadas