ruby - rails - factorybot gem
Encontrar o crear registro a través de la asociación factory_girl (5)
Estoy usando exactamente el escenario de Pepino que describiste en tu pregunta:
Given the following user exists:
| Email | Group |
| [email protected] | Name: mygroup |
Puedes extenderlo así:
Given the following user exists:
| Email | Group |
| [email protected] | Name: mygroup |
| [email protected] | Name: mygroup |
| [email protected] | Name: mygroup |
Esto creará 3 usuarios con el grupo "mygroup". Como se usa así utiliza la funcionalidad ''find_or_create_by'', la primera llamada crea el grupo, las dos llamadas siguientes encuentran el grupo ya creado.
Tengo un modelo de usuario que pertenece a un grupo. El grupo debe tener un atributo de nombre único. La fábrica del usuario y la fábrica del grupo se definen como:
Factory.define :user do |f|
f.association :group, :factory => :group
# ...
end
Factory.define :group do |f|
f.name "default"
end
Cuando se crea el primer usuario, también se crea un nuevo grupo. Cuando trato de crear un segundo usuario, falla porque quiere crear el mismo grupo nuevamente.
¿Hay alguna manera de decirle al método de asociación factory_girl que busque primero un registro existente?
Nota: Intenté definir un método para manejar esto, pero luego no puedo usar f.association. Me gustaría poder usarlo en escenarios de Pepino como este:
Given the following user exists:
| Email | Group |
| [email protected] | Name: mygroup |
y esto solo puede funcionar si la asociación se usa en la definición de Fábrica.
Por lo general, solo hago varias definiciones de fábrica. Uno para un usuario con un grupo y otro para un usuario sin grupo:
Factory.define :user do |u|
u.email "email"
# other attributes
end
Factory.define :grouped_user, :parent => :user do |u|
u.association :group
# this will inherit the attributes of :user
end
Entonces puede usarlos en las definiciones de su paso para crear usuarios y grupos por separado y unirlos a voluntad. Por ejemplo, puede crear un usuario agrupado y un usuario solitario y unir al usuario solitario al equipo de usuarios agrupados.
De todos modos, deberías echarle un vistazo a la gema de pepinillo que te permitirá escribir pasos como:
Given a user exists with email: "[email protected]"
And a group exists with name: "default"
And the user: "[email protected]" has joined that group
When somethings happens....
Puede utilizar initialize_with
con el método 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
También se puede usar con 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
Para Rails 4
La forma correcta en Rails 4 es Group.find_or_create_by(name: name)
, por lo que Group.find_or_create_by(name: name)
initialize_with { Group.find_or_create_by(name: name) }
en lugar.
También puede usar una estrategia FactoryGirl para lograr esto
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
Puedes usar esta esencia . Y luego haz lo siguiente
FactoryGirl.define do
factory :group do
name "name"
end
factory :user do
association :group, factory: :group, strategy: :find_or_create, name: "name"
end
end
Esto está funcionando para mí, sin embargo.
Terminé usando una mezcla de métodos que se encuentran en la red, uno de ellos como fábricas heredadas, como lo sugirió duckyfuzz en otra respuesta.
Lo hice siguiente:
# 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