2011-10-04 21 views
23

Tengo dos modelos (ModelA y ModelB) y fábricas FactoryGirl para cada uno. Quiero que la fábrica de ModelB pueda (A) crear datos de prueba, y (B) construir (sin guardar en la base de datos) datos de muestra para mostrar a los clientes. Tengo problemas para que (A) funcione debido a las validaciones de Rails en mis modelos.Anular validación de rieles al crear objetos FactoryGirl

ModelA:

class ModelA < ActiveRecord::Base 
    belongs_to :model_b 
    validates_presence_of :model_b 
end 

fábrica para ModelA:

FactoryGirl.define do 
    factory :model_a do 
    some_attr "hello" 
    model_b { FactoryGirl.build :model_b } 
    end 
end 

ModelB

class ModelB < ActiveRecord::Base 
    has_one :model_a 
end 

fábrica para ModelB

FactoryGirl.define do 
    factory :model_b do 
    some_attr "goodbye" 
    end 
end 

No puedo crear objetos a partir de estas fábricas sin obtener errores de validación:

ruby> FactoryGirl.create :model_a 
ActiveRecord::RecordInvalid: Validation failed: ModelB can't be blank 

Parece que factorygirl está tratando de salvar el objeto de fábrica antes de guardar sus Asociaciones del. Me doy cuenta de que podría hacer que la fábrica para ModelB cree su ModelA asociado (en lugar de compilarlo); sin embargo, perdería la flexibilidad de poder utilizar la fábrica de ModelA para crear datos de muestra o guardar datos de prueba. Alternativamente, podría eliminar las validaciones; pero entonces no tendría validaciones.

¿Alguna otra opción?

Respuesta

17

Miré en un par de soluciones.

Una es crear un proxy personalizado, mostrada aquí: http://codetunes.com/2009/11/05/fixtures-without-validation-with-factory-girl

El otro es para establecer un bloque to_create en la fábrica:

FactoryGirl.define do 
    factory :model_a do 
    model_b { FactoryGirl.build :model_b }  

    to_create do |instance| 
     instance.model_b.save! 
     instance.save! 
    end 
    end 
end 
+0

Gran solución. Me ayudo mucho. –

+0

El proxy personalizado ya no funciona para su información. default_strategy se eliminó aquí: https://github.com/thoughtbot/factory_girl/commit/76235313240c17db663fe7f2868d7f0ecdea72d8#diff-2856f806bf47808e13bc8dc0baf374db – Geesu

+0

O ver: https://github.com/thoughtbot/factory_girl/issues/578 –

2

Esta validación circular no es una buena idea. ¿Cómo piensa construir objetos reales en la base de datos? Imho eso es simplemente imposible.

Primero debe guardar objetos en la base de datos, la base de datos asigna la identificación y solo después de compilar/guardar el objeto puede hacer referencia al objeto utilizando la ID en una clave externa. Entonces, para poder hacer referencia a un registro en la base de datos, primero debe guardarse. Pero si las validaciones requieren que las ID se conozcan y estén presentes antes de guardar ...

En una relación belongs_to, la clave externa está en la tabla, y luego puede expresar la necesidad de que la clave esté presente. Con la relación has_one, la clave externa está en la otra tabla, y no puede expresar la validación de que el elemento debe estar presente. Por lo tanto, debe eliminar la validación de ModelB.

Espero que esto ayude.

[ACTUALIZACIÓN]

¿Usted intentó escribir:

FactoryGirl.define do 
    factory :model_a do 
    some_attr "hello" 
    model_b 
    end 
end 
+0

Hola, gracias por tu respuesta. Tienes razón, no debería haber tenido las validaciones circulares. He actualizado mi publicación original. Con las correcciones, mi problema original permanece: no puedo crear instancias de fábrica de ModelA usando 'FactoryGirl.create: model_a'. – maxenglander

+0

Seguí tus consejos más recientes y reemplacé mi atributo perezoso con 'association: model_b,: method =>: build' - ¡esto es justo lo que necesitaba! Gracias. – maxenglander

24

¿Qué tal esto?

FactoryGirl.build(:model_a).save(validate: false) 

EDIT: Como Scott McMillin comentarios a continuación, si desea que el objeto construido como una variable, se puede hacer esto:

model_a = FactoryGirl.build(:model_a) 
model_a.save(validate: false) 
+0

No sé de la OP pero esto me ayudó! ¡Gracias! –

+1

Nota: Este one-liner devuelve TrueClass y no una instancia de ModelA. Si necesita usar la instancia, haga: 'model_a = build (: model_a); model_a.save (validate: false) ' –

Cuestiones relacionadas