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.