如何在Factory girl中创建has_and_belongs_to_many关联


119

鉴于以下

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

您如何为公司和用户(包括双向关联)定义工厂?这是我的尝试

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

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

现在我尝试

Factory :user

可能不足为奇的是,由于工厂彼此递归地使用彼此定义自己,因此会导致无限循环。

更令人惊讶的是,我在任何地方都没有提到如何执行此操作,是否有定义所需工厂的模式,或者我做的是根本错误的事情?

Answers:


132

这是对我有用的解决方案。

FactoryGirl.define do

  factory :company do
    #company attributes
  end

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

end

如果您需要特定的公司,可以通过这种方式使用工厂

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

希望这对某人有帮助。


4
谢谢,所有解决方案中最简洁的。
Mik

谢谢。经过数小时的挫败,这解决了我的问题。
托尼·贝宁纳特

仅当所有工厂都在一个文件中时,这才对我有用,这是非常不可取的。因此,下面@opsb提到的解决方案似乎更好。
尖顶


22

我认为,只需创建两个不同的工厂,例如:

 Factory.define:user,:class => User do | u |
  #只是普通的属性初始化
 结束

 Factory.define:company,:class => Company do | u |
  #只是普通的属性初始化
 结束

当您为用户编写测试用例时,只需这样编写

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

希望它能工作。


2
谢谢,这是我可以工作的唯一示例。工厂的女孩是habtm的头疼大问题。
jspooner 2011年

这不再适用于最新版本的FactoryGirl(我正在思考Rails 3)
Raf

9

在提供的网站上找不到上述案例的示例。(只有1:N和多态关联,但没有habtm)。我有一个类似的案例,我的代码如下所示:

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
如果可以验证非零用户数怎么办?
dfens 2011年

5

对我有用的是在使用工厂时设置关联。使用您的示例:

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

company.users << user 
company.save! 

4

发现这种方式很好而冗长:

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)] }对我有很大帮助!谢谢!
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

警告:将用户:[用户]更改为:users => [用户]对于ruby 1.8.x


4
应该不是 after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }吗?
拉夫2012年

0

首先,我强烈建议您使用的has_many:通过替代的habtm(更多相关信息点击这里),所以你会喜欢的东西结束:

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

在这之后,您将在双方都有has_many关联,并可以按照您执行的方式在factory_girl中分配它们。


3
难道不应该是Employment belongs_to :userEmployment belongs_to :company与连接一个企业与一个用户的连接模式?
Daniel Beardsley 2010年

5
通过快速阅读您提到的文章得出的结论是,选择使用habtm还是has_many:through取决于您的用例。没有真正的“赢家”。
auralbee'3

好吧,使用hmt的唯一开销是必须在穿透表上定义id。现在,我无法想象会导致任何问题的情况。我并不是说habtm没有用,只是在99%的用例中,使用hmt更有意义(因为它的优点)。
米兰诺沃塔

6
-1,仅因为HMT具有更多“优势”,仅意味着如果需要这些优势,则应使用它。Pet peeve,因为我现在正在研究一个项目,在某些情况下开发人员使用HMT,而HABTM足够了。因此,代码库更大,更复杂,更不直观,并因此而产生较慢的SQL连接。因此,请尽可能使用HABTM,然后在需要创建单独的联接模型以存储有关每个关联的额外信息时,才使用HMT。
sbeam 2012年

0

Rails 5的更新:

代替使用has_and_belongs_to_many关联,您应该考虑:has_many :through关联。

该关联的用户工厂如下所示:

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

您可以用类似的方式创建公司工厂。

设置两个工厂后,您可以使用创建user_with_companies工厂companies_count option。您可以在此处指定用户所属的公司数量:create(:user_with_companies, companies_count: 15)

您可以在此处找到有关工厂女工协会的详细说明。


0

对于HABTM,我使用了特征和回调

假设您有以下模型:

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

您可以在上方定义工厂

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

您可以定义新工厂,并使用after(:create)回调创建关联列表。让我们看看如何在这个例子中做到这一点:

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

属性companies_count是一个临时属性,可在工厂属性和通过评估程序的回调中使用。现在,您可以使用公司创建用户,并可以选择指定所需的公司数量:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.