【问题标题】:Factory Girl: How to set up a has_many/through associationFactory Girl:如何建立 has_many/through 关联
【发布时间】:2013-01-21 18:23:47
【问题描述】:

我一直在努力使用 Factory Girl 建立 has_many/through 关系。

我有以下型号:

class Job < ActiveRecord::Base
  has_many :job_details, :dependent => :destroy
  has_many :details, :through => :job_details
end

class Detail < ActiveRecord::Base
  has_many :job_details, :dependent => :destroy
  has_many :jobs, :through => :job_details
end

class JobDetail < ActiveRecord::Base
  attr_accessible :job_id, :detail_id
  belongs_to :job
  belongs_to :detail
end

我的工厂:

factory :job do
  association     :tenant
  title           { Faker::Company.catch_phrase }
  company         { Faker::Company.name }
  company_url     { Faker::Internet.domain_name }
  purchaser_email { Faker::Internet.email }
  description     { Faker::Lorem.paragraphs(3) }
  how_to_apply    { Faker::Lorem.sentence }
  location        "New York, NY"
end

factory :detail do
  association :detail_type <--another Factory not show here
  description "Full Time"
end

factory :job_detail do
  association :job
  association :detail
end

我想要创建我的工作工厂,默认Detail 为“全职”。

我一直在尝试遵循这一点,但没有任何运气: FactoryGirl Has Many through

我不确定应该如何使用 after_create 通过 JobDetail 附加详细信息。

【问题讨论】:

    标签: ruby-on-rails bdd factory-bot


    【解决方案1】:

    试试这样的。您想要构建一个detail 对象并将其附加到作业的详细信息关联中。当您使用after_create 时,创建的作业将屈服于块。因此,您可以使用 FactoryGirl 创建一个详细信息对象,并将其直接添加到该作业的详细信息中。

    factory :job do
      ...
    
      after_create do |job|
        job.details << FactoryGirl.create(:detail)
      end
    end
    

    【讨论】:

    • 这很好,谢谢。一个问题 - 添加 after_create 有效,但它以 DEPRECATION WARNING: You're trying to create an attribute detail_id' 响应。不推荐在模型上写入任意属性。请使用attr_writer等。有什么想法吗?
    • 我知道这是旧的,但在 FactoryGirl 中,您现在使用格式为 after(:create) 而不是 after_create 的回调 其余答案应该仍然可以正常工作。
    • 更多关于after(:create)回调的信息:robots.thoughtbot.com/…
    • 好的,这适用于创建,因为有 ID 供 JobDetail 参考,但是 after(:build) 呢?有什么方法可以关联未保存的对象?还是我应该放弃这样的想法?
    【解决方案2】:

    我今天遇到了这个问题,我找到了解决方案。希望这对某人有所帮助。

    FactoryGirl.define do
      factory :job do
    
        transient do
          details_count 5 # if details count is not given while creating job, 5 is taken as default count
        end
    
        factory :job_with_details do
          after(:create) do |job, evaluator|
            (0...evaluator.details_count).each do |i|
              job.details << FactoryGirl.create(:detail)
            end
          end
        end
      end  
    end
    

    这允许创建这样的工作

    create(:job_with_details) #job created with 5 detail objects
    create(:job_with_details, details_count: 3) # job created with 3 detail objects
    

    【讨论】:

    • 适用于目前最新的一切(rails 5、rspec 3.5、factorygirl 4.8)
    【解决方案3】:

    这对我有用

    FactoryGirl.define do
      factory :job do
    
        # ... Do whatever with the job attributes here
    
        factory :job_with_detail do
    
          # In later (as of this writing, unreleased) versions of FactoryGirl
          # you will need to use `transitive` instead of `ignore` here
          ignore do
            detail { create :detail }
          end
    
          after :create do |job, evaluator|
            job.details << evaluator.detail
            job.save
            job_detail = job.job_details.where(detail:evaluator.detail).first
    
            # ... do anything with the JobDetail here
    
            job_detail.save
          end
        end
      end
    end
    

    后来

    # A Detail object is created automatically and associated with the new Job.
    FactoryGirl.create :job_with_detail
    
    # To supply a detail object to be associated with the new Job.
    FactoryGirl.create :job_with_detail detail:@detail
    

    【讨论】:

    • 我知道这是旧的,但我很好奇是什么让这比这里接受的答案更好?
    • 当我阅读原始答案时,示例中没有足够的信息来满足我的喜好。这还在该答案之上添加了其他功能,因为您可以使用 create :job_with_detail 带或不带 detail:@detail 选项,如果未提供,则将自动创建详细信息。
    【解决方案4】:

    您可以通过以下方式解决此问题:

    FactoryBot.define do
      factory :job do
        # job attributes
    
        factory :job_with_details do
          transient do
            details_count 10 # default number
          end
    
          after(:create) do |job, evaluator|
            create_list(:details, evaluator.details_count, job: job)
          end
        end
      end
    end
    

    有了这个,你可以创建一个job_with_details,它有选项来指定你想要多少细节。 你可以阅读this interesting article了解更多详情。

    【讨论】:

      【解决方案5】:

      使用当前的 factory_bot(以前的 factory_girl)实现,一切都由 gem 负责,您无需在 jobs.details 中创建然后推送记录。你只需要这个

      factory :job do
        ...
      
        factory :job_with_details do
          transient do
            details_count { 5 }
          end
      
          after(:create) do |job, evaluator|
            create_list(:detail, evaluator.details_count, jobs: [job])
            job.reload
          end
        end  
      end
      

      以下代码将生成 5 个详细作业

       create(:job_with_details)
      

      【讨论】:

        【解决方案6】:

        自 FactoryBot v5 起,关联保留了构建策略。关联是解决这个问题的最佳方式,docs have good examples for it:

        FactoryBot.define :job do
          job_details { [association(:job_detail)] }
        end
        
        FactoryBot.define :detail do
          description "Full Time"
        end
        
        FactoryBot.define :job_detail do
          association :job
          association :detail
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-18
          相关资源
          最近更新 更多