Reputation: 2422
These are 2 simple models:
class Post < ActiveRecord::Base
has_one :asset, :dependent => :destroy
validates :asset, presence: true
end
class Asset < ActiveRecord::Base
belongs_to :post
end
I'm trying to create a factory like this:
factory :post do
# fields...
asset { FactoryGirl.create(:asset) }
end
factory :asset do
# fields...
post
end
But, running the spec it enters a loop.
I've also tryied this:
factory :post do
# fields...
before(:create) do |post, evaluator|
FactoryGirl.create_list(:asset, 1, post: post)
end
end
But ended up in "Validation failed: Asset can't be blank".
How do I represent my situation?
Upvotes: 21
Views: 11116
Reputation: 2422
I solved this problem using after(:build) callback.
factory :post do
# fields...
after(:build) do |post|
post.asset ||= FactoryGirl.build(:asset, :post => post)
end
end
factory :asset do
# fields...
after(:build) do |asset|
asset.post ||= FactoryGirl.build(:post, :asset => asset)
end
end
By this way, the associated objects will be created before the owning class is saved, so validation pass.
Upvotes: 36
Reputation: 61
The validation is failing because when FactoryGirl
creates a Post
, an asset must be present. So in your FactoryGirl
definitions you can create an Asset
as part of creating a Post
. Insert something like the FactoryGirl post.rb
file:
asset { FactoryGirl.create(:asset) }
or
You can create an Asset
as part of your Post
declaration in your spec file such as the following:
asset = FactoryGirl.create(:asset)
FactoryGirl.create(:post, :asset => asset)
Thanks.
Upvotes: 2
Reputation: 6852
the error Validation failed: Asset can't be blank
is because it looks like you have the association backwards in your factories.
factory :post do
# fields...
end
So when you create a post
there is no asset
so the validation fails. Try this
factory :post do
# fields...
asset
end
Take a look at this wiki page and the associations section. It also explains the difference between create
and build
with associations
Upvotes: 0
Reputation: 1453
You can preload a child association by passing it in as follows:
FactoryGirl.define do
factory :post do
asset { Asset.create! }
end
end
(Better still, using the Asset factory to generate it's associated asset with details pre-set).
The other manual way would be to create a Asset via FactoryGirl.create(:asset), and passing it into the variable creation, i.e.:
asset = FactoryGirl.create(:asset)
post = FactoryGirl.create(:post, asset: asset)
Upvotes: 0