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 中有所有详细信息。
给定以下 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 callingnew
on your build class, you can override the default behavior by defininginitialize_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 中有所有详细信息。