Esta miniserie de dos partes fue escrita para personas que desean trabajar con Factory Girl y comenzar a trabajar sin tener que escarbar la documentación demasiado. Para las personas que empezaron a jugar con las pruebas recientemente, hice todo lo posible para que fuera más amigable para los novatos.
Obviamente, haber estado en los mismos zapatos en algún momento me hizo creer que no hace falta mucho para que las personas nuevas se sientan más cómodas con las pruebas. Tomarse un poco más de tiempo para explicar el contexto y desmitificar la jerga ayuda a reducir los índices de frustración para los principiantes.
Comencemos con un poco de historia y hablemos acerca de las buenas personas en thoughtbot que son responsables de esta popular gema Ruby. En 2007/2008, Joe Ferris, CTO de thoughtbot, lo tuvo con los accesorios y comenzó a preparar su propia solución. Ir a través de varios archivos para probar un solo método fue un punto de dolor común al tratar con aparatos. Dicho de otra manera e ignorando todo tipo de inflexibilidades, esa práctica también lleva a escribir pruebas que no le dicen mucho sobre su contexto que se está probando de inmediato..
El hecho de que no se vendiera en la práctica actual lo obligó a revisar varias soluciones para fábricas, pero ninguna de ellas respaldaba todo lo que quería. Así que se le ocurrió Factory Girl, que hizo que las pruebas con datos de prueba fueran más legibles, SECAS y también más explícitas al darle el contexto para cada prueba. Un par de años más tarde, Josh Clayton, Director de Desarrollo de thoughtbot en Boston, se hizo cargo del mantenimiento del proyecto. Con el tiempo, esta gema ha crecido de manera constante y se ha convertido en un "elemento fijo" en la comunidad Ruby..
Hablemos más sobre los principales puntos de dolor que resuelve Factory Girl. Cuando construye su suite de pruebas, está lidiando con una gran cantidad de registros asociados y con información que cambia con frecuencia. Desea poder crear conjuntos de datos para sus pruebas de integración que no sean frágiles, fáciles de administrar y explícitos. Sus fábricas de datos deben ser dinámicas y ser capaces de referirse a otras fábricas, algo que está en parte más allá de los accesorios YAML de los viejos tiempos.
Otra conveniencia que desea tener es la capacidad de sobrescribir atributos para objetos sobre la marcha. Factory Girl te permite hacer todo eso sin esfuerzo, dado que está escrito en Ruby y que hay mucha brujería de metaprogramación detrás de la escena, y se te proporciona un gran lenguaje de dominio específico que es fácil de entender. también.
La construcción de los datos de tu accesorio con esta gema se puede describir como fácil, eficaz y, en general, más conveniente que jugar con los accesorios. De esa manera puede tratar más con los conceptos que con las columnas reales en la base de datos. Pero basta de hablar, vamos a ensuciarnos un poco las manos..
Las personas que tienen experiencia con las aplicaciones de prueba y que no necesitan aprender sobre el concepto de accesorios, no duden en pasar a la siguiente sección. Este es para novatos que solo necesitan una actualización sobre los datos de prueba.
Los accesorios son datos de muestra, ¡eso es todo! Para una buena parte de su conjunto de pruebas, desea poder completar su base de datos de prueba con datos que se ajusten a sus casos de prueba específicos. Durante bastante tiempo, muchos desarrolladores utilizaron YAML para estos datos, lo que lo hizo independiente de la base de datos. En retrospectiva, ser independiente de esa manera puede haber sido lo mejor de esto. Era más o menos un archivo por modelo. Solo eso podría darte una idea sobre todo tipo de dolores de cabeza de los que se quejan las personas. La complejidad es un enemigo en rápido crecimiento que YAML apenas está preparado para enfrentar de manera efectiva. A continuación verás qué tal .yml
archivo con datos de prueba parece.
Archivo YAML: secret_service.yml
"plain Quartermaster: name: Q favorite_gadget: Habilidades de radio de Broom: Inventar gizmos y hacking
00 Agente: nombre: James Bond favorite_gadget: Submarine Lotus Esprit skills: Cómo matar a las Bond Girls e infiltración secreta "
Parece un hash, ¿no? Es una lista separada por dos puntos de pares clave / valor que están separados por un espacio en blanco. Puede hacer referencia a otros nodos entre sí si desea simular asociaciones de sus modelos. Pero creo que es justo decir que ahí es donde la música se detiene y muchos dicen que su dolor comienza. Para los conjuntos de datos que son un poco más complicados, los accesorios YAML son difíciles de mantener y difíciles de cambiar sin afectar otras pruebas. Por supuesto, puedes hacer que funcionen, después de todo, los desarrolladores los usaron bastante en el pasado, pero mucha gente estuvo de acuerdo en que el precio a pagar por la gestión de los accesorios era un poco demasiado alto..
Para evitar romper sus datos de prueba cuando se producen los cambios inevitables, los desarrolladores estaban felices de adoptar nuevas estrategias que ofrecían más flexibilidad y comportamiento dinámico. Ahí es donde entró Factory Girl y dejó los días de YAML atrás. Otro problema es la fuerte dependencia entre la prueba y la .yml
archivo de accesorio Los invitados misteriosos también son un gran dolor con este tipo de accesorios. Factory Girl le permite evitar eso creando objetos relevantes para las pruebas en línea.
Claro, los accesorios de YAML son rápidos, y he escuchado a personas argumentar que un conjunto de pruebas lentas con datos de Factory Girl les hizo volver a la tierra de YAML. En mi opinión, si está usando Factory Girl tanto que realmente ralentiza sus pruebas, podría estar usando excesivamente las fábricas innecesariamente y podría ignorar las estrategias que no llegan a la base de datos. Verá lo que quiero decir cuando lleguemos a la (s) sección (es) relevante (s). Por supuesto, use lo que necesite, pero considérese advertido si YAML lo quema..
Creo que sería justo agregar que en los primeros días de Rails y Ruby TDD, los accesorios YAML fueron el estándar de facto para configurar los datos de prueba en su aplicación. Jugaron un papel importante y ayudaron a que la industria avanzara. Sin embargo, hoy en día tienen una reputación bastante mala. Los tiempos cambian, así que pasemos a las fábricas, que están destinadas a reemplazar los accesorios..
Supongo que ya tiene Ruby y RSpec para las pruebas instaladas en su sistema. Si no, vuelve después de consultar a Google y deberías estar listo. Es bastante sencillo, diría yo. Puedes instalar la gema manualmente en tu terminal a través de Cáscara:
bash gem instalar factory_girl
o agregarlo a tu Gemfile:
gema de rubíes "factory_girl", "~> 4.0"
y correr instalación de paquete
.
Ahora solo necesitas exigir
Factory Girl para completar su configuración. Aquí estoy usando RSpec, así que agregue lo siguiente en la parte superior de /spec/spec_helper.rb
:
ruby requiere 'factory_girl'
Está cubierto, por supuesto, si desea utilizar Factory Girl with Rails. los factory_girl_rails
gema proporciona una integración de Rails útil para factory_girl
.
Cáscara:
bash gem instalar factory_girl_rails
Gemfile:
gema de rubíes "factory_girl_rails", "~> 4.0"
y exigir
por supuesto en spec / spec_helper.rb
:
ruby requiere 'factory_girl_rails'
Si prefieres escribir algo como (por supuesto que sí)
Rubí:
ruby crear (: usuario)
en lugar de
ruby FactoryGirl.create (: usuario)
Cada vez que use una de sus fábricas, solo necesita incluir el FactoryGirl :: Sintaxis :: Métodos
Módulo en su archivo de configuración de prueba. Si olvida ese paso, debe prefaciar todos los métodos de Factory Girl con el mismo prefacio detallado. Esto funciona con cualquier aplicación Ruby, no solo con Rails, por supuesto..
Para RSpec encontrar el spec / spec_helper.rb
archivo y añadir:
"ruby require 'factory_girl_rails'
RSpec.configure do | config | config.include FactoryGirl :: Syntax :: Methods end "
Para los novatos entre ustedes, tenga cuidado que RSpec.configure
El bloque ya estará enterrado bajo cierta cantidad de comentarios. También puede hacer la misma configuración en un archivo separado de su propio estilo. spec / support / factory_girl.rb
. En ese caso, tendrá que agregar el bloque de configuración completo, por supuesto..
La misma configuración funciona si está utilizando otras bibliotecas para realizar pruebas:
Puedes ir más sofisticado con tu configuración lanzando Limpiador de base de datos
, por ejemplo, pero la documentación implica que esta configuración es suficiente para ponerse en marcha, por lo que continuaré desde aquí.
Puede definir sus fábricas en cualquier lugar, pero se cargarán automáticamente si se ubican en las siguientes ubicaciones:
RSpec:
bash spec / factories.rb spec / factories / *. rb
Prueba :: Unidad:
bash test / factories.rb test / factories / *. rb
Como puede ver, tiene la opción de dividirlos en archivos separados que se adhieran a cualquier lógica, o agrupar sus fábricas en una sola gran fábricas.rb
expediente. La complejidad de su proyecto será la mejor guía para cuándo separar lógicamente las fábricas en sus propios archivos separados.
Factory Girl proporciona una sintaxis DSL ruby bien desarrollada para definir fábricas como usuario, enviar o cualquier otro objeto no solo Registro activo objetos. Las clases de Ruby "normales" están perfectamente bien. Comience por configurar un bloque de definición en su fábricas.rb
expediente.
Rubí:
"ruby FactoryGirl.define do
fin"
Todas las fábricas están definidas dentro de este bloque. Las fábricas solo necesitan un :símbolo
Nombre y un conjunto de atributos para empezar. Este nombre debe ser el snake_cased Versión del modelo que desea emular como SecretServiceAgent en el siguiente ejemplo. La fábrica de abajo se llama secret_service_agent
y tiene atributos llamados nombre
, favorito_gadget
y habilidades
.
Rubí:
"ruby FactoryGirl.define do
factory: secret_service_agent do nombre "Q" favorite_gadget "Submarine Lotus Esprit" habilidades "Inventando gizmos y hacking" fin
fin"
Si le quita una cosa a este artículo, debería ser lo siguiente: Solo defina la fábrica más sencilla posible para que sea válida por defecto-válida en el sentido de validaciones de registro activo, por ejemplo.
Cuando Factory Girl llama salvar!
En instancias, se realizarán sus validaciones. Si alguno de ellos falla, ActiveRecord :: RecordInvalid
se eleva Definir solo lo mínimo le da más flexibilidad si sus datos cambian y reducirá las posibilidades de interrumpir las pruebas y la duplicación, ya que el acoplamiento se reduce al núcleo. No seas perezoso cuando compongas tus fábricas, se pagará en grande!
Si crees que esto suena difícil de manejar, lo más probable es que te alegrará saber que existen soluciones prácticas para segregar los objetos y sus atributos.. Herencia y Rasgos se convertirán en grandes aliados, ya que son estrategias útiles para complementar sus fábricas básicas y mantenerlas en seco al mismo tiempo. Aprende más sobre la herencia a continuación, y mi segundo artículo se centrará bastante en los rasgos.
Si ha incluido el FactoryGirl :: Sintaxis :: Métodos
en el paso de configuración, puede utilizar la sintaxis abreviada para crear fábricas en sus pruebas. Tienes cuatro opciones, que la gente llama construir estrategias:
ruby create (: some_object) # FactoryGirl.create (: some_object)
Éste devuelve una instancia de la clase que emula la fábrica. Se recomienda usar crear
Solo cuando realmente necesitas golpear la base de datos. Esta estrategia ralentiza su conjunto de pruebas si se usa excesivamente innecesariamente. Úselo si desea ejecutar sus validaciones ya que se ejecutará salvar!
en el fondo. Creo que esta opción es más apropiada cuando haces pruebas de integración donde quieres involucrar una base de datos para tus escenarios de prueba.
ruby build (: some_object) # FactoryGirl.build (: some_object)
Crea una instancia y asigna atributos, pero obtendrá una instancia devuelta que no se guarda en la base de datos-construir
guarda el objeto solo en memoria. Si desea verificar si un objeto tiene ciertos atributos, esto hará el trabajo, ya que no necesita acceso a la base de datos para ese tipo de cosas. Detrás de las escenas que no usa. salvar!
, lo que significa que sus validaciones no se ejecutan.
Cuando usas asociaciones con él, puedes encontrarte con un pequeño gotcha. Hay una pequeña excepción en cuanto a no guardar el objeto a través de construir
-eso construye el objeto pero crea Las asociaciones, que cubriré en la sección sobre asociaciones de Factory Girl. Hay una solución para eso en caso de que no sea lo que estaba buscando.
ruby build_stubbed (: some_object) # FactoryGirl.build_stubbed (: some_object)
Esta opción se creó para acelerar sus pruebas y para los casos de prueba en los que ninguno de los códigos necesita llegar a la base de datos. También crea instancias y asigna atributos como construir
Lo hace, pero solo hace que los objetos se vean como si hubieran sido persistidos. El objeto devuelto tiene todos los atributos definidos de su fábrica resguardados, más un falso carné de identidad
y nulo
marcas de tiempo. Sus asociaciones también son apagadas, a diferencia de construir
asociaciones, que están utilizando crear
en objetos asociados. Dado que esta estrategia trata con los apéndices y no depende de la base de datos, estas pruebas serán tan rápidas como sean posibles..
Este método devolverá un hash de solo los atributos definidos en la fábrica relevante, sin asociaciones, marcas de tiempo e ID, por supuesto. Es conveniente si desea crear una instancia de un objeto sin manipular manualmente los hashes de atributos. Lo he visto usado principalmente en las especificaciones del controlador similares a esto:
Rubí:
"ruby it 'redirige a alguna ubicación' publica: crea, espía: atributos_para (: espía)
espera (respuesta) .a redirect_to (some_location) end "
Para cerrar esta sección, permítame darle un ejemplo simple para ver los diferentes objetos devueltos de estas cuatro estrategias de construcción. A continuación puede comparar los cuatro objetos diferentes que obtendrá de atributos_para
, crear
, construir
y build_stubbed
:
"ruby FactoryGirl.define do
fábrica: espía nombre "Marty McSpy" favorito_gadget "Hoverboard" habilidades "Infiltración y espionaje" final
fin"
"ruby attributes_for (: spy)
"
"ruby crear (: espía)
"ruby build (: spy)
"ruby build_stubbed (: spy)
Espero que esto haya sido útil si aún le queda alguna confusión sobre cómo funcionan y cuándo usar qué opción.
¡Te va a encantar este! Con la herencia, puede definir fábricas solo con los atributos necesarios que cada clase necesita para su creación. Esta fábrica matriz puede generar tantas fábricas "secundarias" como crea conveniente para cubrir todo tipo de escenarios de prueba con diferentes conjuntos de datos. Mantener tus datos de prueba en SECO es muy importante, y la herencia hace que esto sea mucho más fácil para ti.
Digamos que desea definir un par de atributos centrales en una fábrica y dentro de esa misma fábrica tienen diferentes fábricas para la misma Clase Con diferentes atributos. En el siguiente ejemplo, puede ver cómo puede evitar la repetición de atributos simplemente anidando sus fábricas. Emulemos un Espía Clase que necesita adaptarse a tres escenarios de prueba diferentes..
fábricas.rb
"ruby FactoryGirl.define do
fábrica: espía nombrar 'Marty McSpy' licence_to_kill habilidades falsas 'espionaje e inteligencia'
factory: quartermaster do name 'Q' skills 'Inventando gizmos y hacking' end Factory: bond do name 'James Bond' licence_to_kill true end end
fin"
some_spec.rb
"ruby bond = create (: bond) quartermaster = create (: quartermaster)
quartermaster.name # => 'Q' quartermaster.skills # => 'Inventar gizmos y hackear' quartermaster.licence_to_kill # => false
bond.name # => 'James Bond' bond.skills # => 'Espionaje e inteligencia' bond.licence_to_kill # => true "
Como se puede observar, la :enlace
y :oficial de intendencia
Las fábricas heredan atributos de sus padres. :espía
. Al mismo tiempo, puede sobrescribir fácilmente los atributos según sea necesario y ser muy expresivo al respecto a través de sus nombres de fábrica. Imagine todas las líneas de código guardadas porque no tiene que repetir la misma configuración básica si desea probar diferentes estados u objetos relacionados. Esa característica por sí sola vale la pena usar Factory Girl y hace que sea difícil volver a los accesorios de YAML.
Si desea evitar las definiciones de las fábricas de anidamiento, también puede vincular las fábricas a sus padres de manera explícita proporcionando un padre
picadillo:
fábricas.rb
"ruby factory: spy do name 'Marty McSpy' licence_to_kill false skills 'fin de espionaje e inteligencia
fábrica: vínculo, padre: espía nombre "James Bond 'licence_to_kill true end"
Es la misma funcionalidad y casi tan seco..
Aquí hay una pequeña pero bienvenida adición a Factory Girl que hace que tratar con listas sea tan fácil como un pastel:
De vez en cuando, en situaciones en las que desees tener varias instancias de alguna fábrica sin mucha confusión, estas te serán útiles. Ambos métodos devolverán una matriz con la cantidad de artículos de fábrica especificados. Muy bien claro?
Rubí:
"ruby spy_clones = create_list (: spy, 3)
fake_spies = build_list (: spy, 3)
spy_clones # => [#
fake_spies # => [#
Hay diferencias sutiles entre las dos opciones, pero estoy seguro de que ya las entiendes. También debo mencionar que puede proporcionar ambos métodos con un hash de atributos si desea sobrescribir los atributos de fábrica sobre la marcha por alguna razón. Las sobrescrituras consumirán un poco de su velocidad de prueba si crea muchos datos de prueba con sobrescrituras. Probablemente tener una fábrica separada con estos atributos cambiados será una mejor opción para crear listas.
"ruby smug_spies = create_list (: spy, 3, skills: 'Smug chistes')
double_agents = build_list (: spy, 3, nombre: 'Vesper Lynd', habilidades: 'Seducción y contabilidad')
smug_spies # => [#
double_agents # => [#
Con frecuencia necesitarás un par de objetos, por lo que Factory Girl te ofrece otras dos opciones:
Es la misma idea que la anterior, pero la matriz devuelta contiene solo dos registros a la vez.
"ruby create_pair (: spy) # => [#
build_pair (: spy) # => [#
Si pensabas que nombrar espías podría ser menos estático, tienes toda la razón. En esta sección final veremos cómo crear valores únicos secuenciales como datos de prueba para sus atributos de fábrica.
¿Cuándo podría ser útil esto? ¿Qué pasa con las direcciones de correo electrónico únicas para probar la autenticación o los nombres de usuario únicos, por ejemplo? Ahí es donde brillan las secuencias. Veamos un par de maneras diferentes en las que puedes usar secuencia
:
"ruby FactoryGirl.define do
secuencia: spy_email do | n | "00#[email protected]" final
Fábrica: espía nombre "Marty McSpy" correo electrónico "[email protected]" habilidades "Espionaje e infiltración" license_to_kill false
Factory: elite_spy do name 'Edward Donne' license_to_kill true end end
fin
top_spy = create (: elite_spy) top_spy.email = generar (: spy_email)
top_spy.email # => "[email protected]" "
Dado que esta secuencia se define "globalmente" para todas sus fábricas (no pertenece a una fábrica específica), puede usar este generador de secuencias para todas sus fábricas donde sea que necesite una : spy_email
. Todo lo que necesitas es generar
y el nombre de tu secuencia.
Como una pequeña variación que es muy conveniente, te mostraré cómo asignar directamente secuencias como atributos a tus fábricas. Use la misma condición que la anterior, donde su secuencia se define "globalmente". En el caso de una definición de fábrica, puede dejar de lado generar
El método call y Factory Girl asignarán el valor devuelto de la secuencia directamente al atributo del mismo nombre. ordenado!
"ruby FactoryGirl.define do
secuencia: correo electrónico hacer | n | "00#[email protected]" final
factory: secret_service_agent do name 'Edwad Donne' email skills 'Espionaje e infiltración' license_to_kill true end
fin"
También puedes usar un secuencia
en atributos perezosos. Cubriré este tema en mi segundo artículo, pero, para completar, también quería mencionarlo aquí. En caso de que necesite un valor único asignado en el momento en que se crea la instancia, se llama perezoso porque este atributo espera con asignación de valor hasta que las secuencias de creación de instancias de objetos solo necesitan un bloque para funcionar también.
"rubí
FactoryGirl.define do
secuencia: mission_deployment do | número | "Mission # number at # DateTime.now.to_formatted_s (: short)" end
Fábrica: espía nombre Do 'Marty McSpy' implementación generar (: misión_despliegue) fin
fin
some_spy = create (: spy) some_spy.deployment # => "Misión # 1 al 19 de octubre 21:13" "
El bloque para el atributo de fábrica se evalúa cuando se crea una instancia del objeto. En nuestro caso, obtendrá una cadena compuesta por un número de misión único y una nueva Fecha y hora
objeto como valores para cada :espía
que se despliega.
Esta opción es mejor cuando solo se necesita una secuencia de valores únicos en un atributo para una sola fábrica. No tendría sentido definirlo fuera de esa fábrica y posiblemente tenga que buscarlo en otro lugar si necesita modificarlo. En el siguiente ejemplo, buscamos generar identificaciones únicas para cada vehículo espía..
"ruby FactoryGirl.define do
fábrica: aston_martin hacer secuencia (: vehicle_id_number) | n | "A_M _ # n" fin
fin
spycar_01 = create (: aston_martin) spycar_01.vehicle_id_number # => “A_M_1”
spycar_02 = create (: aston_martin) spycar_02.vehicle_id_number # => "A_M_2" "
Bueno, tal vez deberíamos proporcionar la vehicle_id_number
atribuir otro valor de inicio que 1
? Digamos que queremos dar cuenta de un par de prototipos antes de que el automóvil estuviera listo para la producción. Puede proporcionar un segundo argumento como valor de inicio para su secuencia. Vamos con 9
esta vez.
"ruby FactoryGirl.define do
fábrica: aston_martin hacer secuencia (: vehicle_id_number, 9) | n | "A_M _ # n" fin
fin
spycar_01 = create (: aston_martin) spycar_01.vehicle_id_number # => "A_M_9"
spycar_02 = create (: aston_martin) spycar_02.vehicle_id_number # => "A_M_10"
"
Como ya ha visto, Factory Girl ofrece un DSL de Ruby bien equilibrado que crea objetos en lugar de registros de base de datos para sus datos de prueba. Ayuda a mantener sus pruebas enfocadas, SECAS y legibles cuando trata con datos ficticios. Eso es un logro bastante sólido en mi libro..
Recuerde que las definiciones de fábrica básicas son clave para su salud mental futura. Cuantos más datos de fábrica coloque en su espacio de prueba global, más probable será que experimente algún tipo de problema de mantenimiento.
Para sus pruebas de unidad, Factory Girl no será necesaria y solo ralentizará su conjunto de pruebas. Josh Clayton sería el primero en atestiguar esto y recomendaría la práctica recomendada de usar Factory Girl de forma selectiva y lo menos posible..