如何使用 FactoryBot 重用类似属性的代码?

How can I reuse code for similar attributes with FactoryBot?

假设我有两个模型 PersonCompany,它们是不同的模型,但是它们都有一个共同的属性:address。如果我要使用 FactoryBot,我会按如下方式实现相应的工厂:

FactoryBot.define do
  factory :person do
    # ...
    address { Faker::Address.street_address }
  end
end

FactoryBot.define do
  factory :company do
    # ...
    address { Faker::Address.street_address }
  end
end

有没有一种方法可以定义一个 address generator 我可以从工厂内的任何地方调用?。例如:

# define reusable attribute
FactoryBot.define do
    attribute :address { Faker::Address.street_address }
end

FactoryBot.define do
  factory :person do
    # ...
    generate_attribute(:address)
  end
end

FactoryBot.define do
  factory :company do
    # ...
    generate_attribute(:address)
  end
end

目前我可以用 sequences 复制这种所需的行为,但它们似乎不适合这项任务。

我想你可以为一个工厂指定一个 nil class,这样你就可以为这两个工厂设置一个共同的父工厂:

factory :addressable, class: nil do
  address { Faker::Address.street_address }
end

factory :person, parent: :addressable do
  ...
end

factory :company, parent: :addressable do
  ...
end

如果你想在工厂内的任何地方使用一个函数,你可以创建一个规范助手并在那里定义你的函数:

# AppSpecHelper
# spec/support/app_spec_helper
module AppSpecHelper
  def generate_attribute
    ...
  end
end

RSpec.configure do |c|
  c.include AppSpecHelper
end

rails_helper.rb中:

RSpec.configure do |config|
  config.include AppSpecHelper
end

对@Shadwell

回答的一个小补充

我相信,在较新的版本中,personcompany 将尝试使用父级的 class 进行初始化,这将导致 Addressable.new。这可以通过子工厂的 class 选项来避免:

# NOTE: class: nil is actually optional
factory :addressable, class: nil do
  address { Faker::Address.street_address }
end

factory :person, parent: :addressable, class: 'Person' do
  # ...
end

factory :company, parent: :addressable, class: 'Company' do
  # ...
end

P.S。 看起来像工厂机器人 got support for several parents,所以可能会做一些关注支持