Jak stworzyć asocjacje has_and_belongs_to_many w Factory girl


120

Biorąc pod uwagę następujące kwestie

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

jak definiujecie fabryki dla firm i użytkowników, w tym stowarzyszenie dwukierunkowe? Oto moja próba

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

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

teraz próbuję

Factory :user

Być może nie jest zaskakujące, że prowadzi to do nieskończonej pętli, ponieważ fabryki rekurencyjnie używają siebie nawzajem do definiowania siebie.

Co bardziej zaskakujące, nigdzie nie znalazłem wzmianki o tym, jak to zrobić, czy istnieje schemat definiowania niezbędnych fabryk, czy też robię coś zasadniczo nie tak?

Odpowiedzi:


132

Oto rozwiązanie, które działa dla mnie.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

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

end

jeśli będziesz potrzebować konkretnej firmy, możesz w ten sposób korzystać z fabryki

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

Mam nadzieję, że to będzie pomocne dla kogoś.


4
Dziękuję, najbardziej zgrabne ze wszystkich rozwiązań.
Mik

Dziękuję Ci. To rozwiązało mój problem po wielu godzinach frustracji.
Tony Beninate

Działa to tylko wtedy, gdy wszystkie fabryki są w jednym pliku, co jest dość niepożądane. Dlatego rozwiązanie wspomniane przez @opsb poniżej wydaje się lepsze.
spier

40

Usługa Factorygirl została od tego czasu zaktualizowana i zawiera teraz wywołania zwrotne w celu rozwiązania tego problemu. Zajrzyj na http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl, aby uzyskać więcej informacji.


37
Link tak naprawdę nie mówi, jak obsłużyć has_and_belongs_to_many ... Nie wiem, jak to zrobić ...
dmonopoly

3
Składnia wywołania zwrotnego została teraz zmieniona na: after(:create)zamiast after_createw factory girl, jak wspomniano tutaj: stackoverflow.com/questions/15003968/…
Michael Yagudaev

22

Moim zdaniem wystarczy stworzyć dwie różne fabryki, takie jak:

 Factory.define: user,: class => User do | u |
  # Po prostu inicjalizacja normalnych atrybutów
 koniec

 Factory.define: company,: class => Company do | u |
  # Po prostu inicjalizacja normalnych atrybutów
 koniec

Kiedy piszesz przypadki testowe dla użytkownika, po prostu pisz w ten sposób

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

Mam nadzieję, że to zadziała.


2
Dzięki temu to jedyny przykład, jaki mogłem uzyskać. Dziewczyna z fabryki to wielki ból głowy dla Habtm.
jspooner

To już nie działa z ostatnimi wersjami FactoryGirl (myślę o Railsach 3)
Raf

9

Nie mogłem znaleźć przykładu dla wyżej wymienionego przypadku na podanej stronie internetowej. (Tylko związki 1: N i polimorficzne, ale bez habtm). Miałem podobny przypadek i mój kod wygląda tak:

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
co, jeśli istnieje walidacja niezerowej liczby użytkowników?
dfens,

5

U mnie zadziałało tworzenie skojarzeń podczas korzystania z fabryki. Na podstawie twojego przykładu:

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

company.users << user 
company.save! 

4

Znalazłem w ten sposób ładne i szczegółowe:

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)] }bardzo mi pomaga! Dziękuję Ci!
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

Ostrzeżenie: Zmień użytkowników: [użytkownik] na: użytkownicy => [użytkownik] dla Ruby 1.8.x


4
Nie powinno tak być after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }:?
Raf

0

Przede wszystkim gorąco zachęcam do korzystania z has_many: through zamiast habtm (więcej o tym tutaj ), więc otrzymasz coś takiego:

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

Po tym będziesz miał asocjacje po obu stronach i będziesz mógł przypisywać je do nich w factory_girl w sposób, w jaki to zrobiłeś.


3
Czy nie powinno tak być Employment belongs_to :useri Employment belongs_to :companyprzy modelu łączenia łączącym jedną firmę z jednym użytkownikiem?
Daniel Beardsley

5
Mój wniosek z szybkiego przeczytania wspomnianego posta jest taki, że od twojego przypadku użycia zależy, czy wybierzesz habtm, czy has_many: through. Nie ma prawdziwego „zwycięzcy”.
auralbee

Cóż, jedynym narzutem podczas używania hmt jest to, że musisz mieć zdefiniowany identyfikator w tabeli przelotowej. W tej chwili nie wyobrażam sobie sytuacji, w której mogłoby to spowodować jakiś problem. Nie mówię, że habtm jest bezużyteczne, tylko że w 99% przypadków użycia hmt ma większy sens (ze względu na jego zalety).
Mediolan Novota

6
-1, tylko dlatego, że HMT ma więcej „zalet” oznacza, że ​​powinieneś go używać tylko wtedy, gdy POTRZEBUJESZ tych zalet. Pet zirytowany, ponieważ pracuję teraz nad projektem, w którym programista używał HMT w kilku przypadkach, w których HABTM by wystarczył. Baza kodu jest zatem większa, bardziej złożona, mniej intuicyjna i z tego powodu tworzy wolniejsze sprzężenia SQL. Tak więc używaj HABTM, kiedy możesz, a gdy POTRZEBUJESZ utworzyć oddzielny model łączenia, aby przechowywać dodatkowe informacje o każdym powiązaniu, tylko wtedy używaj HMT.
belka

0

Aktualizacja dla Rails 5:

Zamiast używać has_and_belongs_to_manyasocjacji, powinieneś rozważyć: has_many :throughskojarzenie.

Fabryka użytkowników dla tego powiązania wygląda następująco:

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

W podobny sposób możesz stworzyć fabrykę firmy.

Po ustawieniu obu fabryk możesz utworzyć user_with_companiesfabrykę za pomocą companies_count option. Tutaj możesz określić, do ilu firm należy użytkownik:create(:user_with_companies, companies_count: 15)

Szczegółowe informacje na temat stowarzyszeń fabrykantów można znaleźć tutaj .


0

W przypadku HABTM użyłem cech i zwrotów .

Załóżmy, że masz następujące modele:

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

Możesz zdefiniować fabrykę powyżej :

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

Możesz zdefiniować nową fabrykę i użyć po (: create) callback do stworzenia listy skojarzeń. Zobaczmy, jak to zrobić w tym przykładzie:

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

Atrybut Companies_count jest przejściowy i jest dostępny w atrybutach fabryki oraz w wywołaniu zwrotnym za pośrednictwem oceniającego. Teraz możesz utworzyć użytkownika z firmami z opcją określenia, ile chcesz firm:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.