如何让 FactoryBot return 成为正确的 STI sub class?

How to make FactoryBot return the right STI sub class?

我正在对系统进行重大更改,因此我将其中一个主表更改为 STI,并创建子类来实现特定行为。

class MainProcess < ApplicationRecord
end

class ProcessA < MainProcess
end

class ProcessB < MainProcess
end

在应用程序代码中,如果我 运行 MainProcess.new(type: 'ProcessA') 它会 return 我想要的 ProcessA 。 但是在 Rspec 测试中,当我 运行 FactoryBot::create(:main_process, type: 'ProcessA') 它正在 returning a 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。

根据 documentation:

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

所以尽量避免使用。 (虽然我没有找到任何其他方法来实现这个结果而不使用它)

如果您只是修改原始代码以将 class 指定为 class 类型而不是字符串,它会起作用:

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

这是relevant section of the FactoryBot documentation

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)