Advertisement
  1. Code
  2. TDD

Factory Girl 201

Scroll to top
Read Time: 19 mins

Spanish (Español) translation by Manuel (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Mi segundo artículo sobre esta popular y útil gema de Ruby trata de un par de temas más matizados que los principiantes no necesitan necesariamente preocuparse de inmediato cuando empiezan. Una vez más, he hecho todo lo posible para mantenerlo accesible a los novatos y explicar cada pedacito de la jerga con la que la gente nueva en el Desarrollo Dirigido por Pruebas (TDD) podría tropezar.

Temas

  • Atributos dependientes
  • Atributos transitorios
  • Atributos de pereza
  • Modificación de las fábricas
  • Devolución de llamadas
  • Asociaciones
  • Alias
  • Rasgos

Atributos dependientes

Si necesitas utilizar valores de atributos para componer otros atributos de fábrica sobre la marcha, Factory Girl te tiene cubierto. Solo tienes que envolver el valor del atributo en un bloque e interpolar los atributos que necesites. Estos bloques tienen acceso a un evaluador, que se les cede, y que a su vez tiene acceso a otros atributos, incluso transitorios.

Atributos transitorios

Creo que es justo llamarlos atributos falsos. Estos atributos virtuales también te permiten pasar opciones adicionales cuando construyes tus instancias de fábrica, a través de un hash, por supuesto. La instancia en sí no se verá afectada por ellos ya que estos atributos no se establecerán en su objeto de fábrica. Por otro lado, Factory Girl trata los atributos transitorios igual que los reales.

Sin embargo, si usas attributes_for, no aparecerán. Los atributos dependientes y las devoluciones de llamada son capaces de acceder a estos atributos falsos dentro de tu fábrica. En general, son otra estrategia para mantener tus fábricas DRY.

El ejemplo anterior resulta un poco más DRY ya que no había necesidad de crear fábricas separadas para los supervillanos que quieren salvar el mundo o son amigos de Blofeld respectivamente. Los atributos transitorios te dan la flexibilidad necesaria para hacer todo tipo de ajustes y evitar crear una gran cantidad de fábricas muy similares.

Atributos de pereza

Los atributos "normales" en Factory Girl se evalúan cuando se define la fábrica. Normalmente se proporcionan valores estáticos como parámetros a métodos con el mismo nombre que tus atributos. Si quieres retrasar la evaluación hasta el último momento posible, cuando la instancia se instancie, tendrás que introducir los valores de los atributos mediante un bloque de código. Las asociaciones y los valores creados dinámicamente a partir de objetos como los objetos DateTime serán tus clientes más frecuentes para ese tratamiento perezoso.

Modificación de fábricas

Este no es probablemente un caso de uso con el que te encuentres todos los días, pero a veces heredas fábricas de otros desarrolladores y quieres cambiarlas, en caso de que estés usando una gema TDD'd por ejemplo. Si sientes la necesidad de ajustar estas fábricas heredadas para que se ajusten mejor a tus escenarios de prueba específicos, puedes modificarlas sin crear otras nuevas ni utilizar la herencia.

Esto se hace a través de FactoryGirl.modify, y tiene que estar fuera de ese bloque FactoryGirl.define en particular que quieres cambiar. Lo que no puedes hacer es modificar la secuencia o el trait, aunque puedes anular los atributos definidos a través del trait. Los callbacks en la fábrica "original" tampoco serán anulados. La llamada de retorno en tu bloque Factory.modify se ejecutará como la siguiente en la línea.

En el ejemplo anterior, necesitábamos que nuestros espías fueran un poco más "sofisticados" y utilizaran un mecanismo mejor para manejar el despliegue. He visto ejemplos en los que los autores de gemas tenían que manejar el tiempo de forma diferente y en los que era práctico modificar los objetos de fábrica simplemente anulando las cosas que necesitas ajustar.

Devolución de llamadas

Los callbacks permiten inyectar código en varios momentos del ciclo de vida de un objeto, como save, after_save, before_validation, etc. Rails, por ejemplo, ofrece un montón de ellos y hace que sea bastante fácil para los novatos abusar de este poder.

Ten en cuenta que las devoluciones de llamada que no están relacionadas con la persistencia de los objetos son un anti-patrón conocido, y es un buen consejo no cruzar esa línea. Por ejemplo, puede parecer conveniente usar un callback después de instanciar algo como el usuario para enviar correos electrónicos o procesar algún pedido, pero este tipo de cosas invitan a los bugs y crean ataduras que son innecesariamente difíciles de refactorizar. Tal vez esa sea una de las razones por las que Factory Girl "solo" te ofrece cinco opciones de devolución de llamada para jugar:

  • before(:create) ejecuta un bloque de código antes de que se guarde tu instancia de fábrica. Se activa cuando se utiliza create(:some_object).
  • after(:create) ejecuta un bloque de código después de guardar tu instancia de fábrica. Se activa cuando se utiliza create(:some_object).
  • after(:build) ejecuta un bloque de código después de que tu objeto de fábrica haya sido construido en memoria. Se activa cuando se utiliza tanto build(:some_object) como create(:some_object).
  • after(:stub) ejecuta un bloque de código después de que tu fábrica haya creado un objeto stubbed. Se activa cuando se utiliza build_stubbed(:some_object).
  • custom(:your_custom_callback) ejecuta una llamada de retorno personalizada sin necesidad de añadirla before o after.

¡Atención!

Ten en cuenta que para todas las opciones de callback, dentro de los bloques de callback, tendrás acceso a una instancia de la fábrica a través de un parámetro del bloque. Esto será útil de vez en cuando, especialmente con las asociaciones.

A continuación, un Ninja tiene un montón de desagradables estrellas arrojadizas (shuriken) a tu disposición. Como tienes un objeto ninja en la devolución de llamada, puedes asignar fácilmente la estrella arrojadiza para que pertenezca al ninja. Echa un vistazo a la sección sobre asociaciones si ese ejemplo te deja con un par de signos de interrogación.

Además, a través del objeto evaluador también tienes acceso a los atributos transitorios. Eso te da la opción de pasar información adicional cuando "creas" objetos de fábrica y necesitas modificarlos sobre la marcha. Esto le da toda la flexibilidad necesaria para jugar con las asociaciones y escribir datos de prueba expresivos.

Si encuentras la necesidad de tener múltiples devoluciones de llamada en tu fábrica, Factory Girl no se interpone en tu camino, incluso las de tipo múltiple. Naturalmente, el orden de ejecución es de arriba a abajo.

El diablo está en los detalles, por supuesto. Si se utiliza create(:some_object), se ejecutarán las llamadas de retorno after(:build) y after(:create).

También se pueden agrupar múltiples estrategias de construcción para ejecutar la misma llamada de retorno.

Por último, pero no por ello menos importante, puedes incluso configurar algo así como callbacks "globales" que anulen los callbacks de todas las fábricas, al menos en ese archivo en particular si las has separado en varios archivos de fábrica.

factories/gun.rb

¡Atención!

Si se utiliza la herencia para componer las fábricas hijas, las devoluciones de llamada del padre se heredarán también.

La última milla

Vamos a juntarlo todo en las siguientes secciones sobre asociaciones y rasgos, sí, también he colado alias porque era el mejor lugar sin saltar por todas partes. Si has prestado atención y recuerdas cosas del primer artículo, ahora todo debería encajar perfectamente.

Asociaciones

Las asociaciones son esenciales para toda aplicación web que se precie y que tenga un poco de complejidad. Un post que pertenece a un usuario, un listado que tiene muchas valoraciones y demás son el pan de cada día que desayunan los desarrolladores. Visto desde esa perspectiva, resulta obvio que para los escenarios más complejos las fábricas deben ser a prueba de balas y fáciles de manejar, al menos para no estropear tu mojo TDD.

Emular las asociaciones de modelos a través de Factory Girl es relativamente sencillo, diría yo. Eso en sí mismo es bastante sorprendente en mi opinión. Lograr un alto nivel de facilidad y comodidad para construir conjuntos de datos complejos hace que la práctica de TDD sea una obviedad y mucho más efectiva.

El nuevo Q tiene habilidades de hacker y necesita tener un ordenador decente, ¿verdad? En este caso tienes una clase Computer y sus instancias pertenecen a instancias de la clase Quartermaster. Fácil, ¿verdad?

¿Qué tal algo más complicado? Digamos que nuestros espías utilizan una gun que tiene_muchos cartridges (balas).

Las devoluciones de llamada son muy útiles con las asociaciones, ¿No? Ahora puedes construir una pistola con o sin munición. A través del hash gun: gun has proporcionado a la fábrica de cartridge la información necesaria para crear la asociación a través de foreign_key.

Si necesitas otro tamaño de revista puedes pasarlo a través de tu atributo transitorio.

¿Y qué pasa con las diferentes estrategias de construcción? ¿No había algo sospechoso? Bien, esto es lo que necesitas recordar: Si usas create para objetos asociados, ambos serán guardados. Así que create(:quartermaster) construirá y guardará tanto a Q como a su ThinkPad.

Será mejor que use la build, entonces, si quiero evitar el golpe en la base de datos, ¿Verdad? Buena idea, pero la build sólo se aplicaría a la quartermaster en nuestro ejemplo; el computer asociado seguiría guardándose. Un poco complicado, lo sé. Esto es lo que puedes hacer si necesitas evitar guardar el objeto asociado, especifica la estrategia de construcción que necesitas para tu asociación.

Nombras el objeto de fábrica asociado y pasas un hash con tu estrategia de construcción. Para que esto funcione es necesario utilizar la llamada de asociación explícita. El ejemplo de abajo no funcionará.

Ahora ambos objetos usan build y no se guarda nada en la base de datos. Podemos comprobar esa suposición utilizando new_record?, que devuelve true si la instancia no ha sido persistida.

Ya que estamos, a través de la llamada de asociación explícita también se puede hacer referencia a diferentes nombres de fábrica y cambiar los atributos sobre la marcha.

Cerremos este capítulo con un ejemplo que es polimórfico.

No te sientas mal si necesitas un poco más de tiempo para asimilarlo. Te recomiendo que te pongas al día con Asociaciones Polimórficas si no estás seguro de lo que ocurre aquí.

Alias

Los alias para tus fábricas te permiten ser más expresivo sobre el contexto en el que estás usando tus objetos de fábrica. Solo tienes que proporcionar un hash de nombres alternativos que describan mejor la relación entre los objetos asociados.

Supongamos que tienes una fábrica de :agent y una fábrica de :law_enforcement_vehicle. ¿No sería bueno referirse al agente como :owner en el contexto de estos vehículos? En el ejemplo siguiente, lo he contrastado con un ejemplo sin alias.

¡Atención!

No olvides añadir dos puntos delante del alias de fábrica (:owner) cuando los utilices para las asociaciones en tus fábricas. La documentación y muchos posts del blog los utilizan sin dos puntos en estos casos. Todo lo que obtienes es probablemente un NoMethodError porque ahora te falta un método setter para ese alias. (Será mejor que abra un pull request.) La primera vez que me encontré con esto, me desconcertó y me costó un poco superarlo. Recuerden que a veces hay que desconfiar selectivamente de la documentación y de los posts del blog. El tuyo también, por supuesto.

Creo que estarás de acuerdo en que el uso de alias no sólo se lee mejor sino que también te da a ti o a la persona que venga después un poco más de contexto sobre los objetos en cuestión. Sí, es necesario utilizar el plural :aliases también si tienes un solo alias.

También se podría escribir de otra manera, mucho más verbosamente.

Bueno, no es tan limpio, ¿verdad?

Por supuesto, también puedes utilizar estos alias para "construir" objetos de fábrica de inmediato.

En el contexto de los comentarios, un :user podría ser referido como :commenter, en el caso de un :crime un :user podría ser aliasado como :suspect, y así sucesivamente. En realidad, no se trata de ciencia espacial, sino más bien de un práctico azúcar sintáctico que reduce la tentación de la duplicación.

Rasgos

Esta es una de mis cosas favoritas de Factory Girl. En pocas palabras, los rasgos son bloques tipo lego para construir tus fábricas y mezclar comportamientos. Son listas separadas por comas de los rasgos/atributos de los símbolos que quieres añadir a una fábrica en particular, y también están definidos en el archivo(s) de tus fábricas.

En mi opinión, trait es la función más poderosa y conveniente para mantener los datos de tu fábrica DRY y al mismo tiempo ser expresivo. Te permite agrupar grupos de atributos, darles nombres distintos y reutilizarlos donde quieras. ¿Recuerdas cuando te insté a que definieras objetos de fábrica de forma básica? Los traits te ayudarán a conseguir exactamente eso sin sacrificar ninguna comodidad.

Como puedes ver, si quieres cambiar algunos atributos que están repartidos en varios objetos, ahora puedes hacerlo en un lugar central. No es necesaria una cirugía de escopeta. La gestión del estado a través de los rasgos no podría ser más conveniente.

Con esta configuración puedes construir coches espía bastante elaborados mezclando los distintos paquetes de atributos como quieras, sin duplicar nada mediante la creación de todo tipo de fábricas nuevas que den cuenta de las distintas opciones que necesites.

Puedes usar traits con create, build, build_stubbed y attributes_for. Si Q se vuelve inteligente, también puedes anular atributos individuales simultáneamente pasando un hash.

Para las combinaciones de rasgos que ocurren con mucha frecuencia en una fábrica en particular, también puedes crear fábricas hijas con nombres que representen mejor los distintos combos de conjuntos de datos. De esta manera, agrupas sus rasgos sólo una vez, en lugar de hacerlo todo el tiempo cuando creas datos de prueba.

Esto permite crear estos objetos de forma mucho más concisa, y también es más legible.

En lugar de:

Se lee mucho mejor, ¿No? Especialmente cuando no hay nombres de variables involucrados.

Incluso puedes reutilizar los traits como atributos en otros traits y fábricas. Si defines los mismos atributos para varios traits, el último que se haya definido tendrá prioridad, por supuesto.

Presta atención al rasgo mobile_surveillance, que reutiliza los rasgos cloaked y night_vision, básicamente como un atributo. También la fábrica ultimate_spy_car, que esta vez separé de la definición de la fábrica spy_car por diversión, reutiliza todos los rasgos más un atributo adicional que lo hace volar también. Pura magia de película, o quizás debería decir magia de Factory Girl.

create_list y build_list también pueden hacer uso de traits. El segundo parámetro tiene que ser el número de instancias de fábrica que quieres.

¿No sería genial usar asociaciones con traits? Por supuesto, puedes empaquetar las devoluciones de llamada y las asociaciones en los rasgos. No lo dudes.

La forma de utilizarlos ya debería ser aburrida.

Reflexiones finales

Últimas palabras de sabiduría: El cambio es un compañero constante: la necesidad de cambiar los atributos o los tipos de datos se produce constantemente. Este tipo de decisiones de diseño evolucionan. Los rasgos te aliviarán el dolor y te ayudarán a gestionar tus conjuntos de datos.

Imagínese que has utilizado un hash de opciones para la instanciación y que ese requisito ha cambiado totalmente. ¿Cuántos lugares potenciales en tus pruebas podrían romperse y ahora necesitarían atención? Directamente, los traits son una herramienta muy eficaz para eliminar la duplicación en tu conjunto de pruebas. Pero con toda esa comodidad, ¡No seas perezoso y olvides tus pruebas unitarias en las columnas que están representadas por tus traits! De este modo, se les presta la misma atención que a los atributos básicos necesarios para los objetos válidos.

Hay mucho más que descubrir en Factory Girl, y estoy seguro de que ahora estás más que bien equipado para unir las piezas cuando las necesites. Diviértete jugando con esta joya. Espero que tus hábitos de TDD se beneficien de ella.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.