我正在对我的系统进行重大更改,因此我将一个主表更改为 STI,并创建子类来实现特定行为。
class MainProcess < ApplicationRecord
end
class ProcessA < MainProcess
end
class ProcessB < MainProcess
end
在应用程序代码中,如果我运行
MainProcess.new(type: 'ProcessA')
它将返回我想要的 ProcessA 。
但在 Rspec 测试中,当我运行 FactoryBot::create(:main_process, type: 'ProcessA')
时,它返回 MainProcess 并破坏了我的测试。
我的因素是这样的
FactoryBot.define do
factory :main_process do
foo { 'bar' }
end
factory :process_a, parent: :main_process, class: 'ProcessA' do
end
factory :process_b, parent: :main_process, class: 'ProcessB' do
end
end
有什么方法可以让FactoryBot具有与正常程序相同的行为吗?
我找到了解决方案
FactoryBot.define do
factory :main_process do
initialize_with do
klass = type.constantize
klass.new(attributes)
end
end
...
end
答案在这里找到http://indigolain.hatenablog.com/entry/defining-factory-for-sti-defined-model(日语)
如此处所述,
initialize_with
是私有FactoryBot API的一部分。
根据文档:
此方法是私有 API 的一部分。如果可能的话,您应该避免使用此方法,因为它可能会在将来被删除或更改。
因此,如果可以的话,请避免使用。 (虽然我没有找到任何其他方法来达到这个结果而不使用它)
除了 gem 文档中的警告(如上所述)之外,GETTING_STARTED.md 实际上建议您使用它
如果你想使用factory_bot来构造一个对象,其中一些属性被传递来初始化,或者如果你想要做一些除了简单地在构建类上调用new之外的事情,你可以通过在你的工厂上定义initialize_with来覆盖默认行为
initialize_with
被标记为 FactoryBot 私有 API 的一部分,不建议外部使用。
我认为你可以使用嵌套工厂来完成此任务。
factory :process do
factory :type_a_process, class: Process::TypeA do
type {"Process::TypeA"}
end
factory :type_b_process, class: Process::TypeB do
type {"Process::TypeB"}
end
end
end
FactoryBot.create(:type_b_process)
如果您只是修改原始代码以将类指定为类类型而不是字符串,则它可以工作:
FactoryBot.define do
factory :main_process do
foo { 'bar' }
end
factory :process_a, parent: :main_process, class: ProcessA do
end
factory :process_b, parent: :main_process, class: ProcessB do
end
end
这样更好:
initialize_with { type.present? ? type.constantize.new : 发票.new }
https://dev.to/epigene/simple-trick-to-make-factorybot-work-with-sti-j09
如果您想使用
traits
,可以这样做:
FactoryBot.define do
factory :main_process do
foo { 'bar' }
type { 'DefaultProcess' }
initialize_with { type.constantize.new }
trait :process_a do
type { 'ProcessA' }
end
trait :process_b do
type { 'ProcessB' }
end
end
end