Come creare has_and_belongs_to_many associazioni in Factory girl


120

Dato quanto segue

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

come definisci le fabbriche per aziende e utenti inclusa l'associazione bidirezionale? Ecco il mio tentativo

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

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

adesso ci provo

Factory :user

Forse non sorprende che ciò si traduca in un ciclo infinito poiché le fabbriche si usano ricorsivamente a vicenda per definire se stesse.

Più sorprendentemente non ho trovato una menzione su come farlo da nessuna parte, c'è uno schema per definire le fabbriche necessarie o sto facendo qualcosa di fondamentalmente sbagliato?

Risposte:


132

Ecco la soluzione che funziona per me.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

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

end

se hai bisogno di un'azienda specifica puoi usare la fabbrica in questo modo

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

Spero che questo possa essere utile per qualcuno.


4
Grazie, la più chiara di tutte le soluzioni.
Mik

Grazie. Questo ha risolto il mio problema dopo ore di frustrazione.
Tony Beninate

Questo funziona solo per me quando tutte le fabbriche sono in un file, il che è abbastanza indesiderabile. Pertanto la soluzione menzionata da @opsb di seguito sembra essere migliore.
spier

40

Da allora Factorygirl è stato aggiornato e ora include i callback per risolvere questo problema. Dai un'occhiata a http://robots . Thoughtbot.com/post/254496652/aint-no-calla-back-girl per maggiori informazioni.


37
Il link in realtà non dice come gestire has_and_belongs_to_many ... Non vedo come farlo ...
dmonopoly

3
Sintassi richiamata ora è stato cambiato in: after(:create)invece che after_createin Factory Girl come accennato qui: stackoverflow.com/questions/15003968/...
Michael Yagudaev

22

Secondo me, basta creare due diverse fabbriche come:

 Factory.define: user,: class => User do | u |
  # Inizializzazione solo degli attributi normali
 fine

 Factory.define: company,: class => Company do | u |
  # Inizializzazione solo degli attributi normali
 fine

Quando scrivi i casi di test per l'utente, scrivi in ​​questo modo

 Factory (: user,: companies => [Factory (: company)])

Spero che funzioni.


2
Grazie, questo è l'unico esempio che sono riuscito a far funzionare. La ragazza di fabbrica è un grande mal di testa per habtm.
jspooner

Questo non funziona più con le versioni recenti di FactoryGirl (penso a Rails 3)
Raf

9

Non sono riuscito a trovare un esempio per il caso sopra menzionato sul sito Web fornito. (Solo 1: N e associazioni polimorfiche, ma non habtm). Ho avuto un caso simile e il mio codice è simile a questo:

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

3
cosa succede se c'è la convalida del conteggio degli utenti diverso da zero?
dfens

5

Quello che ha funzionato per me è stato impostare l'associazione durante l'utilizzo della fabbrica. Usando il tuo esempio:

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

company.users << user 
company.save! 

4

Trovato in questo modo carino e prolisso:

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

  factory :bar do
    name "Bar"
    foos { |a| [a.association(:foo)] }
  end
end

1
foos { |a| [a.association(:foo)] }mi aiuta molto! Grazie!
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

Avviso: cambia gli utenti: [utente] in: utenti => [utente] per ruby ​​1.8.x


4
Non dovrebbe essere after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }:?
Raf

0

Prima di tutto ti incoraggio vivamente a usare has_many: through invece di habtm (di più su questo qui ), così ti ritroverai con qualcosa del tipo:

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

Dopo questo avrai molte associazioni su entrambi i lati e potrai assegnarle a factory_girl nel modo in cui l'hai fatto.


3
Non dovrebbe essere così Employment belongs_to :usere Employment belongs_to :companycon il modello di join che collega un'azienda con un utente?
Daniel Beardsley,

5
La mia conclusione da una rapida lettura del post che hai citato è che dipende dal tuo caso d'uso se scegliere habtm o has_many: through. Non esiste un vero "vincitore".
auralbee

Bene, l'unico sovraccarico quando si usa hmt è che devi avere un id definito nella tabella passante. In questo momento, non riesco a immaginare una situazione in cui ciò potrebbe causare problemi. Non dico che habtm sia inutile, solo che nel 99% dei casi ha più senso usare hmt (a causa dei suoi vantaggi).
Milan Novota

6
-1, solo perché HMT ha più "vantaggi" significa che dovresti usarlo solo se HAI BISOGNO di quei vantaggi. Pet peeve, perché ora sto lavorando a un progetto in cui lo sviluppatore ha utilizzato HMT in diversi casi in cui HABTM sarebbe stato sufficiente. La base di codice è quindi più grande, più complessa, meno intuitiva e produce unioni SQL più lente a causa di ciò. Quindi, usa HABTM quando puoi, quindi quando DEVI creare un modello di join separato per memorizzare informazioni extra su ciascuna associazione, solo allora usa HMT.
sbeam

0

Aggiornamento per Rails 5:

Invece di usare l' has_and_belongs_to_manyassociazione, dovresti considerare: has_many :throughassociazione.

La fabbrica utente per questa associazione ha il seguente aspetto:

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

Puoi creare la fabbrica dell'azienda in modo simile.

Una volta impostate entrambe le fabbriche, puoi creare una user_with_companiesfabbrica con companies_count option. Qui puoi specificare a quante aziende appartiene l'utente:create(:user_with_companies, companies_count: 15)

Puoi trovare spiegazioni dettagliate sulle associazioni di ragazze di fabbrica qui .


0

Per HABTM ho usato tratti e callback .

Supponi di avere i seguenti modelli:

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

Puoi definire la fabbrica sopra :

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

-1

È possibile definire una nuova fabbrica e utilizzare la richiamata after (: create) per creare un elenco di associazioni. Vediamo come farlo in questo esempio:

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

L'attributo companies_count è un transitorio e disponibile negli attributi della fabbrica e nel callback tramite il valutatore. Ora puoi creare un utente con le aziende con la possibilità di specificare quante aziende desideri:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
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.