【问题标题】:How to create has_and_belongs_to_many associations in Factory girl如何在工厂女孩中创建 has_and_belongs_to_many 关联
【发布时间】:2010-12-01 20:18:35
【问题描述】:

鉴于以下

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

也许不出所料,这会导致无限循环,因为工厂递归地使用彼此来定义自己。

更令人惊讶的是,我在任何地方都没有提到如何做到这一点,是否有定义必要工厂的模式,或者我在做一些根本错误的事情?

【问题讨论】:

    标签: ruby-on-rails associations has-and-belongs-to-many factory-bot


    【解决方案1】:

    对于 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
    

    【讨论】:

      【解决方案2】:

      您可以定义新工厂并使用 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
      

      【讨论】:

        【解决方案3】:

        Rails 5 更新:

        您应该考虑使用has_many :through 关联,而不是使用has_and_belongs_to_many 关联。

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

        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
        

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

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

        你可以找到关于factory girl associations here的详细解释。

        【讨论】:

          【解决方案4】:

          发现这种方式既好又冗长:

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

          【讨论】:

          • foos { |a| [a.association(:foo)] } 对我有很大帮助!谢谢!
          【解决方案5】:

          这是适合我的解决方案。

          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])
          

          希望这对某人有所帮助。

          【讨论】:

          • 谢谢你,最简洁的解决方案。
          • 谢谢。经过数小时的挫折,这解决了我的问题。
          • 这仅适用于所有工厂都在 一个文件 中的情况,这是非常不可取的。因此,下面@opsb 提到的解决方案似乎更好。
          【解决方案6】:
            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
          

          警告:将用户:[user] 更改为 :users => [user] for ruby​​ 1.8.x

          【讨论】:

          • 不应该是:after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }?
          【解决方案7】:

          在我看来,只需创建两个不同的工厂,例如:

          Factory.define :user, :class=> 用户做 |u| # 只是普通的属性初始化 结尾 Factory.define :company, :class=> 公司做 |u| # 只是普通的属性初始化 结尾

          当你为用户编写测试用例时,就这样写

          工厂(:用户,:公司=> [工厂(:公司)])

          希望它会起作用。

          【讨论】:

          • 谢谢这是我唯一可以工作的例子。工厂女孩是 habtm 的一大难题。
          • 这不再适用于最新版本的 FactoryGirl(我在想 Rails 3)
          【解决方案8】:

          我在提供的网站上找不到上述案例的示例。 (只有 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
          

          【讨论】:

          • 如果验证非零用户数怎么办?
          【解决方案9】:

          首先,我强烈建议你使用 has_many :through 而不是 habtm(更多关于 here),所以你最终会得到类似的东西:

          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 中分配给他们。

          【讨论】:

          • 不应该是Employment belongs_to :userEmployment belongs_to :company 连接一个公司和一个用户的连接模型吗?
          • 我通过快速阅读您提到的帖子得出的结论是,选择 habtm 还是 has_many :through 取决于您的用例。没有真正的“赢家”。
          • 嗯,使用 hmt 时唯一的开销是您必须在直通表上定义 id。现在,我无法想象会导致任何问题的情况。我并不是说 habtm 没有用,只是在 99% 的用例中使用 hmt 更有意义(因为它的优势)。
          • -1,仅仅因为 HMT 有更多的“优势”,这意味着你应该在需要这些优势时使用它。讨厌,因为我现在正在做一个项目,开发人员在几个情况下使用 HMT,而 HABTM 就足够了。因此,代码库更大、更复杂、更不直观,并因此产生更慢的 SQL 连接。因此,尽可能使用 HABTM,然后当您需要创建单独的连接模型来存储有关每个关联的额外信息时,才使用 HMT。
          【解决方案10】:

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

          user = Factory(:user)
          company = Factory(:company)
          
          company.users << user 
          company.save! 
          

          【讨论】:

            【解决方案11】:

            Factorygirl 已更新,现在包含用于解决此问题的回调。请查看http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl 了解更多信息。

            【讨论】:

            • 该链接实际上并没有说明如何处理 has_and_belongs_to_many...我不知道该怎么做...
            • 回调语法现在已更改为:after(:create),而不是此处提到的工厂女孩​​中的 after_createstackoverflow.com/questions/15003968/…
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-05-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多