Hey spent quite a bit of time trying to figure out some test failures last week and ended up narrowing it down to the way that factory girl handles after_create callbacks.
I expected these callbacks to work as they do in rails where they are a part of the db transaction. However, after looking at the code and your own specs for callbacks its become clear that factory girl's after_create callbacks as they exist now are not intended for directly updating the db and the object is not reloaded afterward. All tests of after_create callbacks test assignment, not db updates.
Unless this is intentional or enforcing some kind of best-practice around after_create callbacks that I'm not aware of, it's a bug that could easily be fixed.
@pathouse the callbacks are there to operate outside of the context of an ORM/ODM and are merely hooks into the lifecycle of factory_girl independent of the actual database interaction.
It's up to each developer to handle the specifics of each ORM/ODM and how they handle updates to either refresh (or not!) whatever they need to in the callbacks.
What's the bug, specifically, that you'd like addressed, understanding that factory_girl has no ties to a specific ORM/ODM and that each has its own understanding (or lack thereof) of transactions, refreshing objects, etc.?
@joshuaclayton I think it's a bit of unexpected behavior that FactoryGirl doesn't return an object that includes all the changes that the FactoryGirl callbacks made.
In the getting started docs we have:
factory :user do
ignore do
rockstar true
upcased false
end
name { "John Doe#{" - Rockstar" if rockstar}" }
email { "#{name.downcase}@example.com" }
after(:create) do |user, evaluator|
user.name.upcase! if evaluator.upcased
end
end
FactoryGirl.create(:user, upcased: true).name
#=> "JOHN DOE - ROCKSTAR"
The above made a lot of sense. When we get the object back, the callback has been evaluated. And the object has been changed. I know that this is all in memory so it's easy.
Now as per an actual database or dealing with an ORM, something like:
factory :user do
after(:create) do |user, evaluator|
3.times { user.create_post }
end
end
FactoryGirl.create(:user).posts
#=> []
is very unexpected behavior. The simplest solution of course is to reload the user object. However, I'm sure we could find an ORM-agnostic solution to this.
@ScotterC the problem is that factory_girl doesn't actually require an ORM/ODM and introducing more functionality to try to figure out how to force the object reload isn't ideal. In cases like this, I'd call user.reload after creating each post, since that's what you'd have to do if you weren't using factory_girl anyway.
Alright. Well I'm happier with this issue existing publicly for the next guy to google
Sorry to zombie... I just stumbled upon this issue. Glad to find it.
It's a little trickier than just calling user.reload isn't it? When the user model is created by FactoryGirl, the associated Post models don't exist yet. If there are AR callbacks in the User model that run over the has_many relationship, they are going to do so over an empty set. Caching the lastest post date, for instance, in the User model's before_save callback, isn't going to happen.
So you need to call user.reload and user.save if you want to simulate a model made the "Rails way". Now you have three hits to the db. Or am I wrong?
Is this similar to this issue? http://stackoverflow.com/questions/35950470/rails-factorygirl-trait-association-with-model-after-create-callback-not-setting I can not seem to figure out why my callbacks are not working...
Most helpful comment
Alright. Well I'm happier with this issue existing publicly for the next guy to google