Cách tạo has_and_belongs_to_many liên kết trong Factory girl


120

Cho những điều sau đây

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

làm thế nào để bạn xác định nhà máy cho các công ty và người dùng bao gồm cả liên kết hai chiều? Đây là nỗ lực của tôi

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

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

bây giờ tôi thử

Factory :user

Có lẽ không có gì đáng ngạc nhiên khi điều này dẫn đến một vòng lặp vô hạn khi các nhà máy sử dụng đệ quy lẫn nhau để xác định chính chúng.

Đáng ngạc nhiên hơn là tôi không tìm thấy đề cập đến cách thực hiện điều này ở bất cứ đâu, có một khuôn mẫu nào để xác định các nhà máy cần thiết hay tôi đang làm điều gì đó sai về cơ bản?

Câu trả lời:


132

Đây là giải pháp phù hợp với tôi.

FactoryGirl.define do

  factory :company do
    #company attributes
  end

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

end

nếu bạn cần một công ty cụ thể, bạn có thể sử dụng nhà máy theo cách này

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

Hy vọng điều này sẽ hữu ích cho ai đó.


4
Cảm ơn bạn, gọn gàng nhất trong tất cả các giải pháp.
Mik

Cảm ơn bạn. Điều này đã khắc phục sự cố của tôi sau nhiều giờ thất vọng.
Tony Beninate

Điều này chỉ hoạt động đối với tôi khi tất cả các nhà máy nằm trong một tệp , điều này khá không mong muốn. Do đó, giải pháp được đề cập bởi @opsb dưới đây có vẻ tốt hơn.
spier

40

Factorygirl kể từ đó đã được cập nhật và hiện bao gồm các lệnh gọi lại để giải quyết vấn đề này. Hãy xem tại http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl để biết thêm thông tin.


37
Liên kết không thực sự nói như thế nào để xử lý has_and_belongs_to_many ... Tôi không thấy làm thế nào để làm điều này ...
dmonopoly

3
Cú pháp gọi lại hiện đã được thay đổi thành: after(:create)thay vì after_createtrong factory girl như đã đề cập ở đây: stackoverflow.com/questions/15003968/…
Michael Yagudaev Ngày

22

Theo tôi, chỉ cần tạo hai nhà máy khác nhau như:

 Factory.define: user,: class => User do | u |
  # Chỉ khởi tạo thuộc tính bình thường
 kết thúc

 Factory.define: company,: class => Company do | u |
  # Chỉ khởi tạo thuộc tính bình thường
 kết thúc

Khi bạn viết test-case cho người dùng thì chỉ cần viết như thế này

 Nhà máy (: người dùng,: công ty => [Nhà máy (: công ty)])

Hy vọng nó sẽ hoạt động.


2
Cảm ơn đây là ví dụ duy nhất tôi có thể làm việc. Cô gái nhà máy là một vấn đề lớn đối với habtm.
jspooner

Điều này không còn hoạt động với các phiên bản gần đây của FactoryGirl (tôi đang nghĩ là Rails 3)
Raf

9

Tôi không thể tìm thấy một ví dụ cho trường hợp được đề cập ở trên trên trang web được cung cấp. (Chỉ có 1: N và giả thiết đa hình, nhưng không có habtm). Tôi đã gặp một trường hợp tương tự và mã của tôi trông như thế này:

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
điều gì sẽ xảy ra nếu có xác thực về số lượng người dùng khác 0?
dfens

5

Điều làm việc cho tôi là thiết lập sự liên kết khi sử dụng nhà máy. Sử dụng ví dụ của bạn:

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

company.users << user 
company.save! 

4

Tìm thấy cách này tốt và dài:

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)] }giúp tôi rất nhiều! Cảm ơn bạn!
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

Cảnh báo: Thay đổi người dùng: [user] thành: users => [user] cho ruby ​​1.8.x


4
Không nên after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }:?
Raf

0

Trước hết, tôi thực sự khuyến khích bạn sử dụng has_many: through thay vì habtm (thêm về điều này ở đây ), vì vậy bạn sẽ kết thúc với một cái gì đó như:

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

Sau đó, bạn sẽ có has_many liên kết ở cả hai bên và có thể gán cho chúng trong factory_girl theo cách bạn đã làm.


3
Điều đó có nên không Employment belongs_to :userEmployment belongs_to :companyvới mô hình tham gia kết nối một Công ty với một Người dùng?
Daniel Beardsley

5
Kết luận của tôi từ việc đọc nhanh bài đăng mà bạn đã đề cập là tùy thuộc vào trường hợp sử dụng của bạn để chọn habtm hay has_many: through. Không có "người chiến thắng" thực sự.
auralbee

Chà, chi phí duy nhất khi sử dụng hmt là bạn phải có id được xác định trên bảng thông qua. Hiện tại, tôi không thể tưởng tượng được tình huống có thể gây ra bất kỳ vấn đề gì. Tôi không nói rằng habtm là vô dụng, chỉ là trong 99% trường hợp sử dụng, sử dụng hmt sẽ hợp lý hơn (vì những ưu điểm của nó).
Milan Novota

6
-1, chỉ vì HMT có nhiều 'lợi thế' hơn chỉ có nghĩa là bạn nên sử dụng nó nếu bạn CẦN những lợi thế đó. Xin chào, bởi vì tôi đang làm việc trong một dự án mà nhà phát triển đã sử dụng HMT trong một số trường hợp mà HABTM đã đủ dùng. Cơ sở mã do đó lớn hơn, phức tạp hơn, ít trực quan hơn và tạo ra các phép nối SQL chậm hơn vì nó. Vì vậy, hãy sử dụng HABTM khi bạn có thể, sau đó khi bạn CẦN tạo một mô hình kết hợp riêng biệt để lưu trữ thông tin bổ sung về từng liên kết, chỉ khi đó hãy sử dụng HMT.
sbeam

0

Cập nhật cho Rails 5:

Thay vì sử dụng has_and_belongs_to_manyliên kết, bạn nên xem xét: has_many :throughliên kết.

Nhà máy người dùng cho liên kết này trông giống như sau:

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

Bạn có thể tạo nhà máy công ty theo cách tương tự.

Khi cả hai nhà máy được thiết lập, bạn có thể tạo user_with_companiesnhà máy với companies_count option. Tại đây, bạn có thể chỉ định số lượng công ty mà người dùng thuộc về:create(:user_with_companies, companies_count: 15)

Bạn có thể tìm thấy lời giải thích chi tiết về các hiệp hội nữ công xưởng tại đây .


0

Đối với HABTM, tôi đã sử dụng các đặc điểm và lệnh gọi lại .

Giả sử bạn có các mô hình sau:

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

Bạn có thể xác định Nhà máy ở trên :

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

Bạn có thể xác định nhà máy mới và sử dụng lệnh gọi lại after (: create) để tạo danh sách các liên kết. Hãy xem cách làm điều đó trong ví dụ này:

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

Thuộc tính company_count là một thuộc tính tạm thời và có sẵn trong các thuộc tính của nhà máy và trong lệnh gọi lại thông qua trình đánh giá. Bây giờ, bạn có thể tạo người dùng với các công ty với tùy chọn chỉ định số lượng công ty bạn muốn:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.