Comment configurer l'usine dans FactoryGirl avec l'association has_many


88

Quelqu'un peut-il me dire si je ne fais que la configuration dans le mauvais sens?

J'ai les modèles suivants qui ont des associations 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

J'utilise Rails 3.1.rc4, FactoryGirl 2.0.2, factory_girl_rails 1.1.0 et rspec. Voici mon contrôle de base rspec rspec sanity pour l' :listingusine:

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

Voici l'usine (: listing)

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

Les usines :listing_featureet :featuresont configurées de la même manière.
Si la association :featuresligne est commentée, tous mes tests réussissent.
Lorsqu'il est

association :features, :factory => :feature

le message d'erreur est undefined method 'each' for #<Feature> que je pensais avoir du sens pour moi car car listing.featuresrenvoie un tableau. Alors je l'ai changé en

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

et l'erreur que j'obtiens maintenant est ArgumentError: Not registered: features N'est-il simplement pas judicieux de générer des objets d'usine de cette façon, ou que me manque-t-il? Merci beaucoup pour toute contribution!

Réponses:


56

La création de ces types d'associations nécessite l'utilisation des rappels de FactoryGirl.

Un ensemble parfait d'exemples peut être trouvé ici.

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

Pour le ramener à votre exemple.

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

avez-vous fini par utiliser association: features, [: factory =>: feature]?
davidtingsu

106

Vous pouvez également utiliser un bloc et ignorer le associationmot - clé. Cela permet de créer des objets sans enregistrer dans la base de données (sinon, une association has_many enregistrera vos enregistrements dans la base de données, même si vous utilisez la buildfonction à la place de create).

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

5
C'est le miaulement du chat. La capacité des deux buildet en createfait le modèle le plus polyvalent. Ensuite, utilisez cette stratégie de construction FG personnalisée gist.github.com/Bartuz/74ee5834a36803d712b7 pour post nested_attributes_fortester les actions du contrôleur quiaccepts_nested_attributes_for
Chris Beck

4
voté positif, beaucoup plus lisible et polyvalent que la réponse acceptée IMO
m_x

1
À partir de FactoryBot 5, le associationmot clé utilise la même stratégie de génération pour le parent et l'enfant. Ainsi, il peut créer des objets sans enregistrement dans la base de données.
Nick

25

Vous pouvez utiliser trait:

FactoryGirl.define do
  factory :listing do
    ...

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

Avec callback, si vous avez besoin de la création de DB:

...

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

Utilisez dans vos spécifications comme ceci:

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

Cela supprimera la duplication dans vos usines et sera plus réutilisable.

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


20

J'ai essayé quelques approches différentes et c'est celle qui a fonctionné le plus fiable pour moi (adaptée à votre cas)

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

Voici comment j'ai configuré le mien:

# 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

Et puis dans vos tests, vous pouvez faire:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

J'espère que cela pourra aider.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.