Mich Dart
Mich Dart

Reputation: 2422

FactoryGirl, has_one association and validation failed

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

Answers (4)

Mich Dart
Mich Dart

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

Iz103
Iz103

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

fontno
fontno

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

HaaR
HaaR

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

Related Questions