I'm making a big change in my system, so I changed one of my main tables into a STI, and create subclasses to implement the specific behavior.
class MainProcess < ApplicationRecord
end
class ProcessA < MainProcess
end
class ProcessB < MainProcess
end
In the application code, if I run MainProcess.new(type: 'ProcessA')
it will return a ProcessA as I want.
But in the Rspec tests when I run FactoryBot::create(:main_process, type: 'ProcessA')
it is returning a MainProcess and breaking my tests.
My factor is something like this
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
Is there some way to make FactoryBot have the same behavior of normal program?
I found the solution
FactoryBot.define do
factory :main_process do
initialize_with do
klass = type.constantize
klass.new(attributes)
end
end
...
end
The answer was founded here http://indigolain.hatenablog.com/entry/defining-factory-for-sti-defined-model (in japanese)
As mentioned here initialize_with
is part of a private FactoryBot API.
According to the 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.
So avoid to use if you can. (although I didn't find any other way to achieve this result without use it)
Besides the warning in the gem documentation (described above), the GETTING_STARTED.md actually suggest you use it
If you want to use factory_bot to construct an object where some attributes are passed to initialize or if you want to do something other than simply calling new on your build class, you can override the default behavior by defining initialize_with on your factory
If you just modify your original code to specify the class as the class type instead of a string, it works:
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
Here's the relevant section of the FactoryBot documentation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With