Cara membuat asosiasi has_and_belongs_to_many di Factory girl

120

Diberikan berikut ini

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

bagaimana Anda mendefinisikan pabrik untuk perusahaan dan pengguna termasuk asosiasi dua arah? Ini usahaku

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

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

sekarang saya coba

Factory :user

Mungkin tidak mengejutkan bahwa ini menghasilkan putaran tak terbatas karena pabrik secara rekursif menggunakan satu sama lain untuk mendefinisikan diri mereka sendiri.

Lebih mengejutkan lagi saya belum menemukan cara melakukan ini di mana pun, apakah ada pola untuk menentukan pabrik yang diperlukan atau saya melakukan sesuatu yang secara fundamental salah?

opsb
sumber

Jawaban:

132

Inilah solusi yang berhasil untuk saya.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

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

end

Jika Anda membutuhkan perusahaan tertentu, Anda dapat menggunakan pabrik dengan cara ini

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

Semoga ini bisa membantu seseorang.

Suborx
sumber
4
Terima kasih, solusi paling rapi dari semua.
Mik
Terima kasih. Ini telah memperbaiki masalah saya setelah berjam-jam frustrasi.
Tony Beninate
Ini hanya berfungsi untuk saya ketika semua pabrik berada dalam satu file yang sangat tidak diinginkan. Oleh karena itu solusi yang disebutkan oleh @opsb di bawah ini tampaknya lebih baik.
spier
40

Factorygirl telah diperbarui dan sekarang menyertakan panggilan balik untuk mengatasi masalah ini. Kunjungi http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl untuk info lebih lanjut.

opsb
sumber
37
Tautan tidak benar-benar mengatakan bagaimana menangani has_and_belongs_to_many ... Saya tidak melihat bagaimana melakukan ini ...
dmonopoly
3
Sintaks panggilan balik sekarang telah diubah menjadi: after(:create)alih-alih after_createdi gadis pabrik seperti yang disebutkan di sini: stackoverflow.com/questions/15003968/…
Michael Yagudaev
22

Menurut saya, buat saja dua pabrik berbeda seperti:

 Factory.define: pengguna,: class => Pengguna melakukan | u |
  # Inisialisasi atribut biasa saja
 akhir

 Factory.define: perusahaan,: class => Perusahaan do | u |
  # Inisialisasi atribut biasa saja
 akhir

Ketika Anda menulis kasus uji untuk pengguna maka tulis saja seperti ini

 Pabrik (: pengguna,: perusahaan => [Pabrik (: perusahaan)])

Semoga berhasil.

Ashish
sumber
2
Terima kasih, ini satu-satunya contoh saya bisa mulai bekerja. Gadis pabrik adalah sakit kepala besar bagi habtm.
jspooner
Ini tidak lagi berfungsi dengan versi terbaru FactoryGirl (saya sedang memikirkan Rails 3)
Raf
9

Saya tidak dapat menemukan contoh untuk kasus yang disebutkan di atas di situs web yang disediakan. (Hanya 1: N dan asosiasi polimorfik, tapi tidak ada habtm). Saya memiliki kasus serupa dan kode saya terlihat seperti ini:

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.dll
sumber
3
bagaimana jika ada validasi jumlah pengguna selain nol?
dfens
5

Yang berhasil bagi saya adalah mengatur asosiasi saat menggunakan pabrik. Menggunakan contoh Anda:

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

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

Menemukan cara ini bagus dan bertele-tele:

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

  factory :bar do
    name "Bar"
    foos { |a| [a.association(:foo)] }
  end
end
pasha.zhukov
sumber
1
foos { |a| [a.association(:foo)] }banyak membantu saya! Terima kasih!
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

Peringatan: Ubah pengguna: [pengguna] menjadi: pengguna => [pengguna] untuk ruby ​​1.8.x

Artur79
sumber
4
Bukankah seharusnya: after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }?
Raf
0

Pertama-tama saya sangat menganjurkan Anda untuk menggunakan has_many: through daripada habtm (lebih lanjut tentang ini di sini ), jadi Anda akan mendapatkan sesuatu seperti:

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

Setelah ini, Anda akan memiliki has_banyak pengaitan di kedua sisi dan dapat menetapkannya di factory_girl seperti yang Anda lakukan.

Milan Novota
sumber
3
Bukankah seharusnya demikian Employment belongs_to :userdan Employment belongs_to :companydengan model gabungan yang menghubungkan satu Perusahaan dengan satu Pengguna?
Daniel Beardsley
5
Kesimpulan saya dari membaca cepat melalui posting yang Anda sebutkan adalah bahwa itu tergantung pada kasus penggunaan Anda apakah akan memilih habtm atau has_many: through. Tidak ada "pemenang" sejati.
auralbee
Nah, satu-satunya overhead saat menggunakan hmt adalah Anda harus menetapkan id pada tabel through. Saat ini, saya tidak bisa membayangkan situasi ketika itu bisa menimbulkan masalah. Saya tidak mengatakan bahwa habtm tidak ada gunanya, hanya saja dalam 99% kasus penggunaan lebih masuk akal untuk menggunakan hmt (karena kelebihannya).
Milan Novota
6
-1, hanya karena HMT memiliki lebih banyak 'keuntungan' berarti Anda harus menggunakannya jika Anda MEMBUTUHKAN kelebihan tersebut. Hewan kesal, karena saya sedang mengerjakan proyek sekarang di mana pengembang menggunakan HMT dalam beberapa kasus di mana HABTM sudah cukup. Oleh karena itu, basis kode lebih besar, lebih kompleks, kurang intuitif dan menghasilkan SQL bergabung yang lebih lambat karenanya. Jadi, gunakan HABTM jika Anda bisa, lalu saat Anda PERLU membuat model gabungan terpisah untuk menyimpan info tambahan tentang setiap asosiasi, baru gunakan HMT.
sbeam
0

Pembaruan untuk Rails 5:

Alih-alih menggunakan has_and_belongs_to_manyasosiasi, Anda harus mempertimbangkan: has_many :throughasosiasi.

Pabrik pengguna untuk asosiasi ini terlihat seperti ini:

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

Anda dapat membuat pabrik perusahaan dengan cara yang sama.

Setelah kedua pabrik ditetapkan, Anda dapat membuatnya user_with_companies pabrik dengan companies_count option. Di sini Anda dapat menentukan berapa banyak perusahaan milik pengguna:create(:user_with_companies, companies_count: 15)

Anda dapat menemukan penjelasan detail tentang asosiasi gadis pabrik di sini .

Nesha Zoric
sumber
0

Untuk HABTM saya menggunakan sifat dan panggilan balik .

Katakanlah Anda memiliki model berikut:

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

Anda dapat menentukan Pabrik di atas :

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.dll
sumber
-1

Anda dapat menentukan pabrik baru dan menggunakan panggilan balik (: create) untuk membuat daftar asosiasi. Mari kita lihat bagaimana melakukannya dalam contoh ini:

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

Atribut company_count adalah sementara dan tersedia di atribut pabrik dan di callback melalui evaluator. Sekarang, Anda dapat membuat pengguna dengan perusahaan dengan opsi untuk menentukan berapa banyak perusahaan yang Anda inginkan:

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