tutorial rails girl factorybot factory_girl_rails bot ruby-on-rails ruby unit-testing factory-bot

ruby-on-rails - rails - factorybot gem



has_many respetando la estrategia de construcción en factory_girl (3)

Situación

# Models class User < ActiveRecord::Base has_many :items end class Items < ActiveRecord::Base belongs_to :user validates_presence_of :user_id end # Factories Factory.define(:user) do |u| u.name "foo" end Factory.define(:user_with_items, :parent => :user) do |u| u.items {|items| [items.association(:item), items.association(:item)]} end Factory.define(:item) do |i| i.color "red" end Factory.define(:item_with_user, :parent => :user) do |i| i.association(:user) end

Problema

Si ejecuta @user = Factory(:user_with_items) , @user.items contiene los dos elementos. El problema es que los elementos no están asociados con el usuario en la base de datos. Si @user.items(true) cargar la asociación @user.items(true) , recuperará una matriz vacía. Sé que puede crearlos manualmente o crear métodos de ayuda por su cuenta para crear el gráfico de objetos, pero me gustaría evitar eso.

Pregunta

Entonces, mi pregunta es ¿cómo se puede construir una relación has_many en factory_girl respetando la estrategia de construcción?


Lo escribí correctamente con herencia y todo eso. Mis compromisos se fusionan here y here .

Ahora está en FactoryGirl 1.2.3, woot!


Por lo general, me gusta separar la creación y la creación, así que todavía puedo construir el objeto sin tener que ir a la base de datos.

Factory.define(:user_with_items, :parent => :user) do |u| u.after_build do |u| u.items = (1..2).map {Factory.build(:item, :user => u)} end u.after_create do |u| u.items.each {|i| i.save!} end end


after_build parchear a after_build Girl para permitir after_build y after_create .

Implementación

Factory.class_eval do def run (proxy_class, overrides) #:nodoc: proxy = proxy_class.new(build_class) proxy.callbacks = @callbacks overrides = symbolize_keys(overrides) overrides.each {|attr, val| proxy.set(attr, val) } passed_keys = overrides.keys.collect {|k| Factory.aliases_for(k) }.flatten @attributes.each do |attribute| unless passed_keys.include?(attribute.name) attribute.add_to(proxy) end end proxy.result end def after_create(&block) @callbacks ||= {} @callbacks[:after_create] = block end def after_build(&block) @callbacks ||= {} @callbacks[:after_build] = block end end Factory::Proxy.class_eval do attr_accessor :callbacks def run_callback(name) callbacks && callbacks[name] && callbacks[name].call(@instance) end end Factory::Proxy::Build.class_eval do def result run_callback(:after_build) @instance end end Factory::Proxy::Create.class_eval do def result run_callback(:after_build) @instance.save! run_callback(:after_create) @instance end end

Esto podría ser un gemelo malvado o simplemente una extensión que requieras.

Ejemplo de uso

# Models class User < ActiveRecord::Base has_many :items end class Items < ActiveRecord::Base belongs_to :user validates_presence_of :user_id end # Factories Factory.define(:user) do |u| u.name "foo" end Factory.define(:user_with_items, :parent => :user) do |u| u.after_build do |o| o.items = [Factory.build(:item, :user => o), Factory.build(:item, :user => o)] end end Factory.define(:item) do |i| i.color "red" end Factory.define(:item_with_user, :parent => :user) do |i| i.association(:user) end # Run user = Factory(:user_with_items) user.items(true) # Shows the two saved items

Espero que esto ayude a alguien en el futuro. Probablemente trataré de enviar esto a los chicos en thoughtbot, pero ya hay un par de entradas obsoletas en su rastreador de errores en el tema ya.