【问题标题】:In Rails 6 and FactoryGirl 4.9, how do I tie the original object back its has_many relation that's being created?在 Rails 6 和 FactoryGirl 4.9 中,如何将原始对象与正在创建的 has_many 关系联系起来?
【发布时间】:2021-11-19 23:20:37
【问题描述】:

我正在使用带有 FactoryGirl 4.9.0 的 Rails 6。我有这些模型,一个有一个 has_many 和另一个......

class Contract < ApplicationRecord
    …
  belongs_to :client

class Client < ApplicationRecord
    …
  has_many :contracts

在我的工厂里,我有

FactoryGirl.define do
  factory :client do
    name "Test Client”
  end

  factory :dp_client, class: Client do

    Contracts { [create(:contract)] }

  end
end

但是当我去使用工厂时,

create(:dp_client)

我得到了错误……

Validation failed: Client must exist

在我的工厂中创建“has_many”关系时,我是否将其绑定回创建它的对象?

【问题讨论】:

标签: ruby-on-rails unit-testing factory-bot ruby-on-rails-6 has-many


【解决方案1】:
FactoryBot.define do
  factory :client do
    name { "Test client" }
  end

  factory :contract do
   client
  end

  factory :dp_client, class: Client do
    name { "client name" }
    after(:create) do |client|
      create(:contract, client: client)
    end
  end
end

【讨论】:

    【解决方案2】:

    这样做是这样的:

    FactoryBot.define do
      factory :client do
        name { "Test client" }
      end
    
      factory :contract do
        association :client
      end
    
      factory :dp_client, class: Client do
        name { "Test DP Client"}
        contracts { [ FactoryBot.create(:contract) ]}
      end
    end
    
    

    【讨论】:

      【解决方案3】:

      有多种方法可以解决这个问题。

      选项 1:

      在你的块中使用实例。 FactoryBot 将self 设置为工厂,并将其作为参数传入。所以这两个都应该工作:

      contracts { |factory| [build(:contract, client: factory.instance)] }
      
      # or
      
      contracts { [build(:contract, client: instance)] } # because self is the factory
      

      注意:我在这里使用build 而不是create,因为Rails 如何自动保存关联。在大多数用例中,两者都应该可以正常工作。

      选项 2:使用after_create

      这是 Saiqul Haq 提出的解决方案。主要缺点:它只适用于create(不适用于build)。另外值得注意的是:它可能无法反映您应用的实际情况(例如,如果您的记录总是一起创建)。

      选项 3:使用before_create

      类似于之前的解决方案,但在工作流程方面可能更现实:

      before(:create) do |client|
        client.build_contract(attributes_for(:contract))
      end
      

      【讨论】:

      • 您的解决方案提到了“FactoryBot”,但我使用的是 FactoryGirl。他们是一样的吗?无论如何,使用您的选项 1,我收到错误消息“#<:syntaxrunner:0x00007f8f32a83270> 的未定义方法 instance' for #&lt;FactoryGirl::SyntaxRunner:0x00007ff47bcea730&gt;" and option 2 gives the error "undefined method factory”。
      • FactoryBot 是 FactoryGirl 的新名称
      • 您是否尝试过使用 yield 语法 (= contracts { |factory| ... })?一个问题可能是您的版本还不支持这种方法(FactoryGirl 的最新版本已经超过 4 年 => 建议升级!)。
      • 不幸的是,我无法从 FactoryGirl 更改为其他内容。我将我正在使用的版本放在问题标题中以帮助指导我的位置。
      • 据我所知,它也应该适用于 FactoryGirl 4.9.x。另请注意,FactoryBot 与 FactoryGirl 直接兼容,您只需替换类名(例如,FactoryBot.define 而不是 FactoryGirl.define)。因此,原则上,您的代码绝对应该适用于 FactoryBot 4.9.x,并且可能适用于低于 5.0.0 的任何版本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 1970-01-01
      相关资源
      最近更新 更多