Estoy probando un modelo con una devolución de llamada posterior a la creación que me gustaría ejecutar solo en algunas ocasiones durante la prueba. ¿Cómo puedo omitir / ejecutar devoluciones de llamada desde una fábrica?
class User < ActiveRecord::Base
after_create :run_something
...
end
Fábrica:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
ruby-on-rails
rspec
factory-bot
luizbranco
fuente
fuente
:on => :create
validación, useafter(:build) { |user| user.class.skip_callback(:validate, :create, :after, :run_something) }
Class.skip_callback
llamada será persistente en otras pruebas, por lo que si sus otras pruebas esperan que se produzca la devolución de llamada, fallarán si intenta invertir la lógica de devolución de llamada de omisión.after(:build)
bloque. Esto permite que su configuración predeterminada de fábrica ejecute la devolución de llamada y no requiere restablecer la devolución de llamada después de cada uso.Cuando no desee ejecutar una devolución de llamada, haga lo siguiente:
Tenga en cuenta que skip_callback será persistente en otras especificaciones después de que se ejecute, por lo tanto, considere algo como lo siguiente:
fuente
Ninguna de estas soluciones es buena. Ellos desfiguran la clase eliminando la funcionalidad que debería eliminarse de la instancia, no de la clase.
En lugar de suprimir la devolución de llamada, estoy suprimiendo la funcionalidad de la devolución de llamada. En cierto modo, me gusta más este enfoque porque es más explícito.
fuente
around_*
(por ejemplouser.define_singleton_method(:around_callback_method){|&b| b.call }
).Me gustaría mejorar la respuesta de @luizbranco para que la devolución de llamada after_save sea más reutilizable al crear otros usuarios.
Ejecutando sin devolución de llamada after_save:
Ejecutando con devolución de llamada after_save:
En mi prueba, prefiero crear usuarios sin la devolución de llamada de forma predeterminada porque los métodos utilizados ejecutan cosas adicionales que normalmente no quiero en mis ejemplos de prueba.
---------- ACTUALIZACIÓN ------------ Dejé de usar skip_callback porque había algunos problemas de inconsistencia en el conjunto de pruebas.
Solución alternativa 1 (uso de stub y unstub):
Solución alternativa 2 (mi enfoque preferido):
fuente
Rails 5:
skip_callback
error de argumento al saltar de una fábrica FactoryBot.Hubo un cambio en Rails 5 en la forma en que skip_callback maneja las devoluciones de llamada no reconocidas:
Cuando
skip_callback
se llama desde la fábrica, la devolución de llamada real en el modelo AR aún no está definida.Si ha intentado todo y se ha arrancado el pelo como yo, aquí está su solución (la obtuve al buscar problemas de FactoryBot) ( NOTA la
raise: false
parte ):Siéntase libre de usarlo con cualquier otra estrategia que prefiera.
fuente
Esta solución funciona para mí y no tienes que agregar un bloque adicional a tu definición de fábrica:
fuente
Un simple stub funcionó mejor para mí en Rspec 3
fuente
User
;:run_something
no es un método de clase.Nota importante : debe especificar ambos. Si solo se usa antes y ejecuta varias especificaciones, intentará deshabilitar la devolución de llamada varias veces. Tendrá éxito la primera vez, pero en la segunda, la devolución de llamada ya no se definirá. Entonces saldrá un error
fuente
Llamar a skip_callback desde mi fábrica resultó problemático para mí.
En mi caso, tengo una clase de documento con algunas devoluciones de llamada relacionadas con s3 antes y después de la creación que solo quiero ejecutar cuando es necesario probar la pila completa. De lo contrario, quiero omitir esas devoluciones de llamada s3.
Cuando probé skip_callbacks en mi fábrica, persistió ese salto de devolución de llamada incluso cuando creé un objeto de documento directamente, sin usar una fábrica. Entonces, en cambio, usé talones de moca en la llamada de compilación posterior y todo funciona perfectamente:
fuente
before_validation
gancho (tratando de hacerloskip_callback
con cualquiera de las opciones de FactoryGirlbefore
oafter
parabuild
ycreate
no funcionó)Esto funcionará con la sintaxis actual de rspec (a partir de esta publicación) y es mucho más limpio:
fuente
La respuesta de James Chevalier sobre cómo omitir la devolución de llamada before_validation no me ayudó, por lo que si rezagas lo mismo que yo, aquí hay una solución que funciona:
en modelo:
en fábrica:
fuente
Model.skip_callback(...)
En mi caso, tengo la devolución de llamada cargando algo en mi caché de redis. Pero luego no tenía / quería una instancia de redis ejecutándose para mi entorno de prueba.
Para mi situación, similar a la anterior, acabo de copiar mi
load_to_cache
método en mi spec_helper, con:Además, en cierta situación en la que quiero probar esto, solo tengo que deshacerlos en el bloque anterior de los casos de prueba Rspec correspondientes.
Sé que es posible que tenga algo más complicado en su
after_create
interior o que no lo encuentre muy elegante. Puede intentar cancelar la devolución de llamada definida en su modelo, definiendo unafter_create
gancho en su Factory (consulte los documentos de factory_girl), donde probablemente pueda definir la misma devolución de llamada y devoluciónfalse
, de acuerdo con la sección 'Cancelación de devoluciones de llamada' de este artículo . (No estoy seguro del orden en el que se ejecutan las devoluciones de llamada, por lo que no elegí esta opción).Por último, (lo siento, no puedo encontrar el artículo) Ruby te permite usar una metaprogramación sucia para desenganchar un gancho de devolución de llamada (tendrás que restablecerlo). Supongo que esta sería la opción menos preferida.
Bueno, hay una cosa más, no realmente una solución, pero vea si puede salirse con la suya con Factory.build en sus especificaciones, en lugar de crear el objeto. (Sería lo más simple si se pudiera).
fuente
Con respecto a la respuesta publicada anteriormente, https://stackoverflow.com/a/35562805/2001785 , no es necesario que agregue el código a la fábrica. Me resultó más fácil sobrecargar los métodos en las propias especificaciones. Por ejemplo, en lugar de (junto con el código de fábrica en la publicación citada)
Me gusta usar (sin el código de fábrica citado)
De esta manera, no es necesario mirar tanto la fábrica como los archivos de prueba para comprender el comportamiento de la prueba.
fuente
Encontré que la siguiente solución es una forma más limpia, ya que la devolución de llamada se ejecuta / configura a nivel de clase.
fuente
Aquí hay un fragmento que creé para manejar esto de una manera genérica.
Omitirá todas las devoluciones de llamada configuradas, incluidas las devoluciones de llamada relacionadas con rieles
before_save_collection_association
, pero no omitirá algunas necesarias para que ActiveRecord funcione correctamente, como lasautosave_associated_records_for_
devoluciones de llamada generadas automáticamente.Entonces despúes:
No hace falta decirlo, YMMV, así que eche un vistazo a los registros de prueba qué es lo que realmente se está saltando. Tal vez tenga una gema que agregue una devolución de llamada que realmente necesita y hará que sus pruebas fallen miserablemente o de su modelo de grasa de 100 devoluciones de llamada, solo necesita un par para una prueba específica. Para esos casos, pruebe el transitorio
:force_callbacks
PRIMA
A veces también necesita omitir las validaciones (todo en un esfuerzo por hacer las pruebas más rápidas), luego intente con:
fuente
Puede configurar la devolución de llamada con un rasgo para esas instancias cuando desee ejecutarlo.
fuente