103 votes

Comment configurer une usine dans FactoryBot avec une association has_many

Est-ce que quelqu'un peut me dire si je m'y prends simplement de la mauvaise façon pour la configuration ?

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

J'utilise Rails 3.1.rc4, FactoryGirl 2.0.2, factory_girl_rails 1.1.0, et rspec. Voici mon test de régularité rspec de base pour la factory :listing :

it "crée un listing valide à partir de la factory" do
  Factory(:listing).should be_valid

Voici Factory(: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 factories :listing_feature et :feature sont configurées de manière similaire.
Si la ligne association :features est commentée, alors tous mes tests passent.
Quand elle est :

association :features, :factory => :feature

le message d'erreur est undefined method 'each' for # ce qui me semblait logique car listing.features renvoie un tableau. Donc je l'ai changé en :

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

et maintenant j'obtiens l'erreur ArgumentError: Not registered: features Est-ce simplement insensé de générer des objets de factory de cette manière, ou qu'est-ce que je manque ? Merci beaucoup pour toute suggestion !

116voto

JellicleCat Points 4694

Alternativement, vous pouvez utiliser un bloc et sauter le mot-clé association. Cela permet de construire des objets sans enregistrer dans la base de données (sinon, une association has_many enregistrera vos enregistrements dans la bdd, même si vous utilisez la fonction build au lieu de create).

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

59voto

winfred Points 1828

Créer ce type d'associations nécessite l'utilisation des callbacks de FactoryGirl.

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

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

Pour illustrer cela avec 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

42voto

ehoffmann Points 551

Vous pourriez 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 créer une base de données :

...

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

Utilisez-le dans vos spécifications de la manière suivante :

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

Cela permettra de supprimer la duplication dans vos fabriques et de les rendre plus réutilisables.

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

20voto

Dave Sag Points 2652

J'ai essayé plusieurs approches différentes et c'est celle qui a fonctionné de manière la plus fiable pour moi (adaptée à votre cas)

FactoryGirl.define do
  factory :user do
    # quelques détails
  end

  factory :layout do
    # quelques détails
  end

  factory :feature do
    # quelques détails
  end

  factory :listing do
    headline    'titre'
    home_desc   'ceci est la description de la maison'
    association :user, factory: :user
    association :layout, factory: :layout
    after(:create) do |liztng|
      FactoryGirl.create_list(:feature, 1, listing: liztng)
    end
  end
end

8voto

Magne Points 2550

Voici l'approche que la documentation officielle propose : https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md

Il suffit de rechercher "has_many" sur cette page.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X