Richlewis
Richlewis

Reputation: 15374

FactoryGirl and associations validation

I have the following factory setup

FactoryGirl.define do
  factory :image do
    title 'Test Title'
    description 'Test Description'
    photo File.new("#{Rails.root}/spec/fixtures/louvre_under_2mb.jpg")
    after(:build) do
      FactoryGirl.build_list(:category, 1)
    end
  end
end

Within my model i have these validations

class Image < ActiveRecord::Base
  has_many :categories
  validates :title, presence: { message: "Don't forget to add a title" }
  validates :description, presence: { message: "Don't forget to add a description" }
  validates :categories, presence: { message: 'Choose At Least 1 Category' }
end

When I run this test it fails

RSpec.describe Image, type: :model do
  it 'should have a valid Factory' do
    expect(FactoryGirl.build(:image)).to be_valid
  end
end

Failure/Error: expect(FactoryGirl.build(:image)).to be_valid
expected #<Image id: nil, title: "Test Title", description: "Test Description", photo_file_name: "louvre_under_2mb.jpg", photo_content_type: "image/jpeg", photo_file_size: 65618, photo_updated_at: "2015-12-15 08:01:07", created_at: nil, updated_at: nil> to be valid, but got errors: Categories Choose At Least 1 Category

Am i approaching this wrong as i was thinking that the validations would not kick in until the whole object was created? or am i thinking about this incorrectly ?

Thanks

Upvotes: 1

Views: 193

Answers (2)

Nafaa Boutefer
Nafaa Boutefer

Reputation: 2359

the problem is in this part.

after(:build) do
  FactoryGirl.build_list(:category, 1)
end

this will create a list of categories of size 1, but those categories are not associated to the image object. The proper way is as follows:

transient do
  categories_count 1
end
after(:build) do |image, evaluator|
  image.categories = build_list(:category, evaluator.categories_count)
end

or

transient do
  categories_count 1
end
categories { build_list(:category, categories_count) }

personally, I would choose this last option.

the photo attribute as well is problematique. FactoryGirl is all about flexibility of creating records. But the way you're using it will not present any flexibility, so the photo attribute will be shared between all the records that you'll create using this factory. And sooner or later you'll face some headaches.

so the proper way of creating the photo attribute is as follows.

transient do
  photo_name 'default_photo.jpg'
end
photo { File.new(File.join(Rail.root, "spec/fixtures", photo_name) }

than you can use it this way

FactoryGirl.build(:image, photo_name: 'new_photo_name.jpg')

Upvotes: 2

Alexander Shlenchack
Alexander Shlenchack

Reputation: 3869

I would recommend don't use after method in image factory. You should create a correct association. Using this you'll resolve validation error and will't have others problems in the future.

class Image
  accepts_nested_attributes_for :categories
end

FactoryGirl.define do
  factory :image do
    categories_attributes { [FactoryGirl.attributes_for(:category)] }
  end
end

Upvotes: 0

Related Questions