FactoryBot 关联加载现有的 ActiveRecord 模型

FactoryBot association load existing ActiveRecord model

给定以下 Rails 个模型...

class Project < ActiveRecord::Base
  belongs_to :project_category
  after_initialize :do_stuff_based_on_category

  def do_stuff_based_on_category
    # things that reference `project_category` object
    project_category.foo_bar
  end
end

class ProjectCategory < ActiveRecord::Base
  has_many :projects

  def foo_bar
    # ...
  end
end

ProjectCategory 记录的数量和内容是固定的:测试设置使用生产中存在的相同记录。因此,我希望我的 Project 工厂使用明确的、持久的 ProjectCategory 对象( 不是 工厂)。

但是我无法让我的 Project 工厂获取 ProjectCategory 记录,即使正常的 new 调用工作正常:

obj = Project.new project_category: ProjectCategory.find(1)
obj.project_category
# => #<ProjectCategory:0x007f851de8c9a8

# (Older syntax, running FactoryGirl 4.5.0)
FactoryGirl.define do
  factory :project do
    project_category { ProjectCategory.find(1) }
    # ...
  end
end

obj = FactoryGirl.build(:project)
NoMethodError:
   undefined method `foo_bar' for nil:NilClass
 # ./app/models/project.rb:7:in `do_stuff_based_on_category'

问题出在我在 after_initialize 挂钩中使用关联。

来自 factory_bot documentation(加粗我的重点):

For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling new on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values.

与直接调用 Project.new 不同,后者会包含属性,factory_bot 默认情况下不传递任何内容并在事后设置属性。

幸运的是,factory_bot 公开了初始化程序来解决这些类型的场景。

If you want to use factory_bot to construct an object where some attributes are passed to initialize or if you want to do something other than simply calling new on your build class, you can override the default behavior by defining initialize_with on your factory.

所以你可以复制这个:

obj = Project.new project_category: ProjectCategory.find(1)

在工厂内这样做:

FactoryGirl.define do
  factory :project do
    project_category { ProjectCategory.find(1) }
    # ...

    initialize_with { new(project_category: project_category) }
  end
end

如果您有很多属性并且只想将它们全部传递到 new 调用中,factory_bot 有一个快捷方式:

initialize_with { new(attributes) }

上面的文档 link 中有所有详细信息。