FactoryGirl 和协会验证

FactoryGirl and associations validation

我有以下出厂设置

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

在我的模型中我有这些验证

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

当我运行这个测试失败时

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

我是不是处理错了,因为我认为在创建整个对象之前不会启动验证?还是我想错了?

谢谢

我建议不要在图像工厂中使用 after 方法。您应该创建一个正确的关联。使用它您将解决验证错误并且将来不会有其他问题。

class Image
  accepts_nested_attributes_for :categories
end

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

问题出在这部分

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

这将创建大小为 1 的类别列表,但这些类别与图像对象无关。正确的做法是:

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

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

就个人而言,我会选择最后一个选项。

photo 属性也是有问题的。 FactoryGirl 是关于创建记录的灵活性。但是您使用它的方式不会提供任何灵活性,因此照片属性将在您将使用该工厂创建的所有记录之间共享。迟早你会遇到一些头痛的问题。

因此创建 photo 属性的正确方法如下。

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

你可以这样使用它

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