Come impostare la fabbrica in FactoryGirl con l'associazione has_many


90

Qualcuno può dirmi se sto eseguendo la configurazione nel modo sbagliato?

Ho i seguenti modelli che hanno associazioni 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

Sto usando Rails 3.1.rc4, FactoryGirl 2.0.2, factory_girl_rails 1.1.0 e rspec. Ecco il mio controllo di integrità rspec rspec di base per la :listingfabbrica:

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

Ecco Factory (: listato)

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

Le fabbriche :listing_featuree :featuresono configurate in modo simile.
Se la association :featuresriga è commentata, tutti i miei test vengono superati.
Quando è

association :features, :factory => :feature

il messaggio di errore è undefined method 'each' for #<Feature> che ho pensato avesse senso per me perché perché listing.featuresrestituisce un array. Quindi l'ho cambiato in

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

e l'errore che ricevo ora è ArgumentError: Not registered: features Non è semplicemente sensato generare oggetti di fabbrica in questo modo, o cosa mi manca? Grazie mille per qualsiasi contributo!

Risposte:


58

La creazione di questo tipo di associazioni richiede l'utilizzo dei callback di FactoryGirl.

Una serie perfetta di esempi può essere trovata qui.

https:// Thoughtbot.com/blog/aint-no-calla-back-girl

Per portarlo a casa al tuo esempio.

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

hai finito per usare association: features, [: factory =>: feature]?
davidtingsu

110

In alternativa, puoi utilizzare un blocco e saltare la associationparola chiave. Ciò rende possibile costruire oggetti senza salvare nel database (altrimenti, un'associazione has_many salverà i tuoi record nel db, anche se usi la buildfunzione al posto di create).

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

5
Questo è il miagolio del gatto. La capacità di entrambi builde lo createrende il modello più versatile. Quindi usa questa strategia di build FG personalizzata gist.github.com/Bartuz/74ee5834a36803d712b7 per post nested_attributes_fortestare le azioni del controller cheaccepts_nested_attributes_for
Chris Beck

4
voto positivo, molto più leggibile e versatile della risposta accettata IMO
m_x

1
A partire da FactoryBot 5, la associationparola chiave utilizza la stessa strategia di compilazione per genitore e figlio. Quindi, può creare oggetti senza salvarli nel database.
Nick

30

Potresti usare trait:

FactoryGirl.define do
  factory :listing do
    ...

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

Con callback, se hai bisogno della creazione di DB:

...

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

Usa nelle tue specifiche in questo modo:

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

Ciò rimuoverà la duplicazione nelle tue fabbriche e sarà più riutilizzabile.

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


20

Ho provato diversi approcci e questo è quello che ha funzionato in modo più affidabile per me (adattato al tuo 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

Ecco come ho impostato il mio:

# 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

E poi nei tuoi test puoi fare:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

Spero possa aiutare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.