So erstellen Sie has_and_belongs_to_many Assoziationen in Factory Girl

119

Angesichts der folgenden

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

Wie definieren Sie Fabriken für Unternehmen und Benutzer, einschließlich der bidirektionalen Vereinigung? Hier ist mein Versuch

Factory.define :company do |f|
  f.users{ |users| [users.association :company]}
end

Factory.define :user do |f|
  f.companies{ |companies| [companies.association :user]}
end

jetzt versuche ich es

Factory :user

Es ist vielleicht nicht überraschend, dass dies zu einer Endlosschleife führt, da sich die Fabriken rekursiv gegenseitig verwenden, um sich selbst zu definieren.

Überraschender ist, dass ich nirgendwo erwähnt habe, wie das geht. Gibt es ein Muster für die Definition der erforderlichen Fabriken oder mache ich etwas grundlegend Falsches?

opsb
quelle

Antworten:

132

Hier ist die Lösung, die für mich funktioniert.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

  factory :user do
   companies {[FactoryGirl.create(:company)]}
   #user attributes
  end

end

Wenn Sie eine bestimmte Firma benötigen, können Sie die Fabrik auf diese Weise verwenden

company = FactoryGirl.create(:company, #{company attributes})
user = FactoryGirl.create(:user, :companies => [company])

Hoffe das wird für jemanden hilfreich sein.

Suborx
quelle
4
Vielen Dank, die ordentlichste aller Lösungen.
Mik
Danke dir. Dies hat mein Problem nach Stunden der Frustration behoben.
Tony Beninate
Dies funktioniert bei mir nur, wenn sich alle Fabriken in einer Datei befinden, was ziemlich unerwünscht ist. Daher scheint die von @opsb unten erwähnte Lösung besser zu sein.
Turm
40

Factorygirl wurde inzwischen aktualisiert und enthält jetzt Rückrufe, um dieses Problem zu lösen. Weitere Informationen finden Sie unter http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl .

opsb
quelle
37
Der Link sagt eigentlich nicht, wie man mit has_and_belongs_to_many umgeht ... Ich sehe nicht, wie man das macht ...
dmonopoly
3
Die Rückrufsyntax wurde jetzt geändert in: after(:create)anstelle von Fabrikmädchen,after_create wie hier erwähnt: stackoverflow.com/questions/15003968/…
Michael Yagudaev
22

Meiner Meinung nach erstellen Sie einfach zwei verschiedene Fabriken wie:

 Factory.define: user ,: class => User do | u |
  # Nur normale Attributinitialisierung
 Ende

 Factory.define: company ,: class => Company do | u |
  # Nur normale Attributinitialisierung
 Ende

Wenn Sie die Testfälle für den Benutzer schreiben, schreiben Sie einfach so

 Fabrik (: Benutzer ,: Firmen => [Fabrik (: Firma)])

Hoffe es wird funktionieren.

Ashish
quelle
2
Danke, dies ist das einzige Beispiel, an dem ich arbeiten kann. Fabrikmädchen ist ein großer Kopfschmerz für habtm.
Jspooner
Dies funktioniert nicht mehr mit neueren Versionen von FactoryGirl (ich denke Rails 3)
Raf
9

Ich konnte auf der bereitgestellten Website kein Beispiel für den oben genannten Fall finden. (Nur 1: N und polymorphe Assoziationen, aber kein Habtm). Ich hatte einen ähnlichen Fall und mein Code sieht folgendermaßen aus:

Factory.define :user do |user|
 user.name "Foo Bar"
 user.after_create { |u| Factory(:company, :users => [u]) }
end

Factory.define :company do |c|
 c.name "Acme"
end
Auralbee
quelle
3
Was ist, wenn die Anzahl der Benutzer ungleich Null überprüft wird?
dfens
5

Was für mich funktioniert hat, war das Festlegen der Zuordnung bei der Verwendung der Fabrik. Anhand Ihres Beispiels:

user = Factory(:user)
company = Factory(:company)

company.users << user 
company.save! 
Larry Kooper
quelle
4

Fand diesen Weg schön und ausführlich:

FactoryGirl.define do
  factory :foo do
    name "Foo" 
  end

  factory :bar do
    name "Bar"
    foos { |a| [a.association(:foo)] }
  end
end
pasha.zhukov
quelle
1
foos { |a| [a.association(:foo)] }hilft mir sehr! Danke dir!
Monteirobrena
3
  factory :company_with_users, parent: :company do

    ignore do
      users_count 20
    end

    after_create do |company, evaluator|
      FactoryGirl.create_list(:user, evaluator.users_count, users: [user])
    end

  end

Warnung: Ändern Sie Benutzer: [Benutzer] in: Benutzer => [Benutzer] für Ruby 1.8.x.

Artur79
quelle
4
Sollte es nicht sein : after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }?
Raf
0

Zuallererst empfehle ich Ihnen dringend, has_many: through anstelle von habtm zu verwenden (mehr dazu hier ), damit Sie am Ende Folgendes erhalten :

Employment belongs_to :users
Employment belongs_to :companies

User has_many :employments
User has_many :companies, :through => :employments 

Company has_many :employments
Company has_many :users, :through => :employments

Danach haben Sie auf beiden Seiten eine has_many-Zuordnung und können diese in factory_girl so zuweisen, wie Sie es getan haben.

Milan Novota
quelle
3
Sollte das nicht so sein Employment belongs_to :userund Employment belongs_to :companymit dem Join-Modell, das ein Unternehmen mit einem Benutzer verbindet?
Daniel Beardsley
5
Mein Fazit aus einem kurzen Durchlesen des von Ihnen erwähnten Beitrags ist, dass es von Ihrem Anwendungsfall abhängt, ob Sie habtm oder has_many: through wählen. Es gibt keinen wirklichen "Gewinner".
Auralbee
Nun, der einzige Aufwand bei der Verwendung von hmt besteht darin, dass in der Through-Tabelle eine ID definiert sein muss. Im Moment kann ich mir keine Situation vorstellen, in der dies zu Problemen führen könnte. Ich sage nicht, dass habtm keinen Nutzen hat, nur dass es in 99% der Anwendungsfälle sinnvoller ist, hmt zu verwenden (wegen seiner Vorteile).
Milan Novota
6
-1, nur weil HMT mehr "Vorteile" hat, heißt das nur, dass Sie es verwenden sollten, wenn Sie diese Vorteile benötigen. Pet ärgern Sie sich, weil ich gerade an einem Projekt arbeite, bei dem der Entwickler HMT in mehreren Fällen verwendet hat, in denen HABTM ausgereicht hätte. Die Codebasis ist daher größer, komplexer, weniger intuitiv und erzeugt dadurch langsamere SQL-Verknüpfungen. Verwenden Sie also HABTM, wenn Sie können, und wenn Sie ein separates Join-Modell erstellen müssen, um zusätzliche Informationen zu jeder Zuordnung zu speichern, verwenden Sie erst dann HMT.
Sbeam
0

Update für Rails 5:

Anstatt has_and_belongs_to_manyAssoziation zu verwenden, sollten Sie Folgendes berücksichtigen: has_many :throughAssoziation.

Die Benutzerfactory für diese Zuordnung sieht folgendermaßen aus:

FactoryBot.define do
  factory :user do
    # user attributes

    factory :user_with_companies do
      transient do
        companies_count 10 # default number
      end

      after(:create) do |user, evaluator|
         create_list(:companies, evaluator.companies_count, user: user)
      end
    end
  end
end

Sie können die Firmenfabrik auf ähnliche Weise erstellen.

Sobald beide Fabriken eingestellt sind, können Sie eine user_with_companiesFabrik mit erstellen companies_count option. Hier können Sie angeben, zu wie vielen Unternehmen der Benutzer gehört:create(:user_with_companies, companies_count: 15)

Detaillierte Erklärungen zu Fabrikmädchenverbänden finden Sie hier .

Nesha Zoric
quelle
0

Für HABTM habe ich Merkmale und Rückrufe verwendet .

Angenommen, Sie haben die folgenden Modelle:

class Catalog < ApplicationRecord
  has_and_belongs_to_many :courses
  
end
class Course < ApplicationRecord
  
end

Sie können die Fabrik oben definieren :

FactoryBot.define do
  factory :catalog do
    description "Catalog description"
    

    trait :with_courses do
      after :create do |catalog|
        courses = FactoryBot.create_list :course, 2

        catalog.courses << courses
        catalog.save
      end
    end
  end
end
lucasarruda
quelle
-1

Sie können eine neue Factory definieren und nach dem Rückruf (: create) eine Liste mit Zuordnungen erstellen. Mal sehen, wie es in diesem Beispiel geht:

FactoryBot.define do

  # user factory without associated companies
  factory :user do
    # user attributes

    factory :user_with_companies do
      transient do
        companies_count 10
      end

      after(:create) do |user, evaluator|
        create_list(:companies, evaluator.companies_count, user: user)
      end
    end
  end
end

Das Attribut company_count ist vorübergehend und in den Attributen der Factory und im Rückruf über den Evaluator verfügbar. Jetzt können Sie einen Benutzer mit Unternehmen erstellen und festlegen, wie viele Unternehmen Sie möchten:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
Aleksandar M.
quelle