Reputation: 5800
I have some models that use an after_initialize
hook like so to set a load of default attributes and other things:
after_initialize do |some_model|
initializer(some_model)
end
def initializer(some_model)
#... loads of pre-processing
end
This all runs great in development and production, however in my FactoryBot and rspec tests this hook isn't running. I have a factory that looks like this:
FactoryBot.define do
factory :some_model do
some_number { 1 }
is_it_something { false }
account { create(:user).accounts.first }
end
end
But my tests are looking like this:
some_model = create(:some_model)
some_model.initializer(some_model)
The issue is I'm having to call the initializer
seperatly after creating the FactoryBot model. This is causing all sorts of issues because the create()
triggers the create validations, and they fail because the initializer isn't ran.
How can I get FactoryBot to run after_initialize
when creating a new instance?
Thanks.
(I might have got some terminology wrong here, so please correct me if I have.)
Upvotes: 2
Views: 1829
Reputation: 15838
You could use the after(:build)
callback of the factory to call the initializer since factorybot sets the attributes after initialization.
FactoryBot.define do
factory :some_model do
some_number { 1 }
is_it_something { false }
account { create(:user).accounts.first }
after(:build) do |some_model|
some_model.initializer(some_model)
end
end
end
that way you don't have to call it every time you create new object from the factory
Anyway, this initializer
method looks like a code smell. I'm not sure what does it do, but maybe there's a better way to do that that also prevents this problem.
Upvotes: 4
Reputation: 18464
FactoryBot calls setters for attributes, not the constructor, so build :some_model, some_attribute: 123, other_attribute: :foo
is in fact equivalent of:
SomeModel.new.tap{|m|
m.some_attribute = 123
m.other_attribute = :foo
}
This way initializer is called on a empty object and only after that all other attributes are getting set.
When you need to set some attributes that are dependent on others - i'd opt into custom setters like
def some_other_attribute=(val)
self.some_attribute = calculate(val)
super
end
or do it in before_validation
Upvotes: 2