Slo*_*vic 60 ruby cucumber ruby-on-rails-3 factory-bot
我有一个属于一个组的用户模型.组必须具有唯一的名称属性.用户工厂和组工厂定义为:
Factory.define :user do |f|
f.association :group, :factory => :group
# ...
end
Factory.define :group do |f|
f.name "default"
end
Run Code Online (Sandbox Code Playgroud)
创建第一个用户时,也会创建一个新组.当我尝试创建第二个用户时,它失败了,因为它想要再次创建相同的组.
有没有办法告诉factory_girl关联方法首先查看现有记录?
注意:我确实尝试定义一个方法来处理这个,但后来我不能使用f.association.我希望能够在像这样的Cucumber场景中使用它:
Given the following user exists:
| Email | Group |
| test@email.com | Name: mygroup |
Run Code Online (Sandbox Code Playgroud)
这只有在工厂定义中使用关联时才有效.
Fer*_*ida 103
你可以用initialize_with与find_or_create方法
FactoryGirl.define do
factory :group do
name "name"
initialize_with { Group.find_or_create_by_name(name)}
end
factory :user do
association :group
end
end
Run Code Online (Sandbox Code Playgroud)
它也可以与id一起使用
FactoryGirl.define do
factory :group do
id 1
attr_1 "default"
attr_2 "default"
...
attr_n "default"
initialize_with { Group.find_or_create_by_id(id)}
end
factory :user do
association :group
end
end
Run Code Online (Sandbox Code Playgroud)
对于Rails 4
Rails 4中的正确方法是Group.find_or_create_by(name: name),所以你要使用
initialize_with { Group.find_or_create_by(name: name) }
Run Code Online (Sandbox Code Playgroud)
代替.
Slo*_*vic 17
我最终使用了网络上发现的混合方法,其中一种是duckyfuzz在另一个答案中建议的继承工厂.
我做了以下事情:
# in groups.rb factory
def get_group_named(name)
# get existing group or create new one
Group.where(:name => name).first || Factory(:group, :name => name)
end
Factory.define :group do |f|
f.name "default"
end
# in users.rb factory
Factory.define :user_in_whatever do |f|
f.group { |user| get_group_named("whatever") }
end
Run Code Online (Sandbox Code Playgroud)
为了确保 FactoryBot 的行为build仍然create正常,我们应该只覆盖 , 的逻辑create,方法是:
factory :user do
association :group, factory: :group
# ...
end
factory :group do
to_create do |instance|
instance.id = Group.find_or_create_by(name: instance.name).id
instance.reload
end
name { "default" }
end
Run Code Online (Sandbox Code Playgroud)
这确保build维持“构建/初始化对象”的默认行为,并且不执行任何数据库读取或写入,因此它总是很快。仅覆盖 的逻辑create以获取现有记录(如果存在),而不是尝试始终创建新记录。
我写了一篇文章解释这一点。
您还可以使用FactoryGirl策略来实现此目的
module FactoryGirl
module Strategy
class Find
def association(runner)
runner.run
end
def result(evaluation)
build_class(evaluation).where(get_overrides(evaluation)).first
end
private
def build_class(evaluation)
evaluation.instance_variable_get(:@attribute_assigner).instance_variable_get(:@build_class)
end
def get_overrides(evaluation = nil)
return @overrides unless @overrides.nil?
evaluation.instance_variable_get(:@attribute_assigner).instance_variable_get(:@evaluator).instance_variable_get(:@overrides).clone
end
end
class FindOrCreate
def initialize
@strategy = FactoryGirl.strategy_by_name(:find).new
end
delegate :association, to: :@strategy
def result(evaluation)
found_object = @strategy.result(evaluation)
if found_object.nil?
@strategy = FactoryGirl.strategy_by_name(:create).new
@strategy.result(evaluation)
else
found_object
end
end
end
end
register_strategy(:find, Strategy::Find)
register_strategy(:find_or_create, Strategy::FindOrCreate)
end
Run Code Online (Sandbox Code Playgroud)
你可以使用这个要点.然后执行以下操作
FactoryGirl.define do
factory :group do
name "name"
end
factory :user do
association :group, factory: :group, strategy: :find_or_create, name: "name"
end
end
Run Code Online (Sandbox Code Playgroud)
不过,这对我有用.
我遇到了类似的问题并提出了这个解决方案。它按名称查找组,如果找到,则将用户与该组关联起来。否则,它会使用该名称创建一个组,然后与其关联。
factory :user do
group { Group.find_by(name: 'unique_name') || FactoryBot.create(:group, name: 'unique_name') }
end
Run Code Online (Sandbox Code Playgroud)
我希望这对某人有用:)