My problem seems very common, but I haven't found any answer in the documentation or the internet itself.
It might seem a clone of this question has_many while respecting build strategy in factory_girl but 2,5 years after that post factory_girl changed a lot.
I have a model with a has_many relation called photos. I want to populate this has many relation preserving my choice of build strategy.
If I call offering = FactoryGirl.build_stubbed :offering, :stay
I expect offering.photos
to be a collection of stubbed models.
The only way i've found to achieve this is this one:
factory :offering do
association :partner, factory: :named_partner
association :destination, factory: :geolocated_destination
trait :stay do
title "Hotel Gran Vía"
description "Great hotel in a great zone with great views"
offering_type 'stay'
price 65
rooms 70
stars 4
event_spaces 3
photos do
case @build_strategy
when FactoryGirl::Strategy::Create then [FactoryGirl.create(:hotel_photo)]
when FactoryGirl::Strategy::Build then [FactoryGirl.build(:hotel_photo)]
when FactoryGirl::Strategy::Stub then [FactoryGirl.build_stubbed(:hotel_photo)]
end
end
end
end
No need to say that IT MUST EXIST a better way of do that.
Ideas?
Other answers have a flaw, the inverse association is not being properly initialized, e.g.
offering.photos.first.offering == offering
isfalse
. Even worse that being incorrect, theoffering
is a newOffering
for each of thephotos
.Also, explicitly specifying a strategy is redundant.
To overcome the flow and to simplify things:
@instance
is an instance of theOffering
being created by the factory at the moment. For the curious, context isFactoryGirl::Evaluator
.If you don't like the
@instance
like I do, you may look inevaluator.rb
and find the following:I really like how
itself
looks:Do be able to use
itself
, undefine it on theEvaluator
:It will be passed to the
@instance
and will return the@instance
itself.For the sake of providing a full example with several photos:
Usage:
Be careful, some things might not work as expected:
offering.photos.first.offering_id
will surprisingly benil
;offering.photos.count
will hit the database with aSELECT COUNT(*) FROM hotel_photos ...
(and will return 0 in most cases), please uselength
orsize
in assertions.This kind of thing works for me:
Here's a slightly cleaner version of Flipstone's answer:
You can use the various FactoryGirl callbacks:
You can also invoke the FactoryRunner class directly and pass it the build strategy to use.