113

Dada la siguienteCómo crear asociaciones has_and_belongs_to_many en Factory Girl

class User < ActiveRecord::Base 
    has_and_belongs_to_many :companies 
end 

class Company < ActiveRecord::Base 
    has_and_belongs_to_many :users 
end 

¿Cómo se define fábricas para empresas y usuarios, incluyendo la asociación bidireccional? Aquí está mi intento

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

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

ahora trato

Factory :user 

Tal como era de esperar esto se traduce en un bucle infinito como las fábricas utilizan de forma recursiva entre sí para definir a sí mismos.

Más sorprendentemente no he encontrado una mención de cómo hacer esto en cualquier lugar, ¿hay un patrón para definir las fábricas necesarias o estoy haciendo algo fundamentalmente malo?

Respuesta

40

Factorygirl se ha actualizado desde entonces y ahora incluye devoluciones de llamada para resolver este problema. Eche un vistazo a http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl para más información.

+36

El enlace en realidad no dice cómo manejar has_and_belongs_to_many ... No veo cómo hacer esto ... – dmonopoly

+3

La sintaxis de devolución de llamada ahora se ha cambiado a: 'after (: create)' en vez de 'after_create' en fábrica chica como se menciona aquí: http://stackoverflow.com/questions/15003968/undefined-method-after-create-with-factorygirl –

0

En primer lugar os animo encarecidamente que utilice has_many: a través de lugar de HABTM (más sobre esto here), por lo que va a terminar con algo como:

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 

Después de esto tendrá has_many asociación en ambos lados y puede asignárselas en factory_girl de la forma en que lo hizo.

+3

¿No debería ser 'Empleo pertenece_ a: usuario' y' Empleo pertenece_ a: empresa' con el modelo de unión que conecta una Compañía con un Usuario? –

+5

Mi conclusión a partir de una lectura rápida de la publicación que mencionaste es que depende de tu caso de uso elegir entre habtm o has_many: a través de. No hay un verdadero "ganador". – auralbee

+0

Bueno, la única sobrecarga cuando se usa hmt es que debe tener una identificación definida en la tabla directa. En este momento, no puedo imaginar una situación en la que eso pueda causar algún problema. No digo que habtm no sirve de nada, solo que en 99% de los casos tiene más sentido usar hmt (debido a sus ventajas). –

5

Lo que funcionó para mí fue establecer la asociación cuando se utilizaba la fábrica. Usando su ejemplo:

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

company.users << user 
company.save! 
9

no pude encontrar un ejemplo para el caso mencionado anteriormente en el sitio web proporcionado. (Solo 1: N y asociaciones polimórficas, pero no habtm). Tuve un caso similar y mi código tiene este aspecto:

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

¿Qué pasa si hay validación de conteo de usuarios distinto de cero? – dfens

22

En mi opinión, basta con crear dos fábricas diferentes, como:

 
Factory.define :user, :class => User do |u| 
    # Just normal attributes initialization 
end 

Factory.define :company, :class => Company do |u| 
    # Just normal attributes initialization 
end 

Al escribir los casos de prueba para los usuarios luego simplemente escribir como esto

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

Espero que funcione.

+2

Gracias, este es el único ejemplo en el que podría trabajar. La chica de la fábrica es un gran dolor de cabeza para habtm. – jspooner

+0

Esto ya no funciona con las versiones recientes de FactoryGirl (estoy pensando en Rails 3) – Raf

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 

Advertencia: cambiar de usuario: [usuario] para los usuarios: => [usuario] para 1.8.x rubí

+3

No debería ser: 'after_create {| company, evaluator | FactoryGirl.create_list (: usuario, evaluator.users_count, empresas: [empresa]) } ' ? – Raf

120

Aquí está la solución que funciona para mí.

FactoryGirl.define do 

    factory :company do 
    #company attributes 
    end 

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

end 

si va a necesitar compañía específica puede utilizar la fábrica de esta manera

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

la esperanza que esto sea útil para alguien.

+4

Gracias, la mejor de todas las soluciones. –

+0

Gracias. Esto ha solucionado mi problema después de horas de frustración. –

+0

Esto solo funciona para mí cuando todas las fábricas están en * un archivo *, lo cual es bastante indeseable. Por lo tanto, la solución mencionada por @opsb a continuación parece ser mejor. – spier

1

Encontrado esta manera agradable y prolijo:

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

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

Actualización para los carriles 5:

En lugar de utilizar has_and_belongs_to_many asociación, se debe considerar: has_many :through asociación.

La fábrica de usuario para esta asociación se ve así:

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 

puede crear la fábrica de la empresa de una manera similar.

Una vez que ambas fábricas están configuradas, puede crear user_with_companies fábrica con companies_count option. Aquí puede especificar a cuántas empresas pertenece el usuario: create(:user_with_companies, companies_count: 15)

Puede encontrar explicaciones detalladas sobre factory girl associations here.

Cuestiones relacionadas