Shoulda + factorygirl: ¿Puedo hacer mis pruebas más rápido?
-
13-09-2019 - |
Pregunta
Estoy buscando una manera de acelerar mi Shoulda + pruebas factorygirl .
El modelo que estoy tratando de probar (StudentExam
) tiene asociaciones con otros modelos. deben existir estos objetos asociados antes de que pueda crear un StudentExam
. Por esa razón, son creados en setup
.
Sin embargo, uno de nuestros modelos (School
) toma un tiempo considerable para crear. Debido setup
se llama antes de cada declaración should
, todo el caso de prueba lleva eones para ejecutar -. Crea un nuevo @school
, @student
, @topic
y @exam
para cada instrucción ejecutada debe
Estoy buscando una manera de crear estos objetos una vez y sólo una vez . ¿Hay algo así como un método para startup
before_all
que me permitiera crear registros, que va a continuar durante el resto del caso de prueba?
Básicamente estoy buscando algo exactamente igual antes de RSpec (: todos) . No estoy preocupado por el tema de las dependencias ya que estas pruebas no modificarán estos objetos caros.
He aquí un ejemplo de caso de prueba. Disculpas por el código largo (También he creado una GIST ):
# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
should_belong_to :student
should_belong_to :exam
setup do
# These objects need to be created before we can create a StudentExam. Tests will NOT modify these objects.
# @school is a very time-expensive model to create (associations, external API calls, etc).
# We need a way to create the @school *ONCE* -- there's no need to recreate it for every single test.
@school = Factory(:school)
@student = Factory(:student, :school => @school)
@topic = Factory(:topic, :school => @school)
@exam = Factory(:exam, :topic => @topic)
end
context "A StudentExam" do
setup do
@student_exam = Factory(:student_exam, :exam => @exam, :student => @student, :room_number => "WB 302")
end
should "take place at 'Some School'" do
assert_equal @student_exam, 'Some School'
end
should "be in_progress? when created" do
assert @student_exam.in_progress?
end
should "not be in_progress? when finish! is called" do
@student_exam.finish!
assert !@student_exam.in_progress
end
end
end
Solución
Si el problema es la creación de estos registros sólo una vez, se puede utilizar una variable de clase. No es un enfoque limpio, pero al menos debería funcionar.
# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
should_belong_to :student
should_belong_to :exam
# These objects need to be created before we can create a StudentExam. Tests will NOT modify these objects.
# @school is a very time-expensive model to create (associations, external API calls, etc).
# We need a way to create the @school *ONCE* -- there's no need to recreate it for every single test.
@@school = Factory(:school)
@@student = Factory(:student, :school => @@school)
@@topic = Factory(:topic, :school => @@school)
@@exam = Factory(:exam, :topic => @@topic)
context "A StudentExam" do
setup do
@student_exam = Factory(:student_exam, :exam => @@exam, :student => @@student, :room_number => "WB 302")
end
should "take place at 'Some School'" do
assert_equal @student_exam, 'Some School'
end
should "be in_progress? when created" do
assert @student_exam.in_progress?
end
should "not be in_progress? when finish! is called" do
@@student_exam.finish!
assert !@student_exam.in_progress
end
end
end
EDIT: Para solucionar el super-feo solución posponer la evaluación con un método de instancia
.# A StudentExam represents an Exam taken by a Student.
# It records the start/stop time, room number, etc.
class StudentExamTest < ActiveSupport::TestCase
...
private
def school
@@school ||= Factory(:school)
end
# use school instead of @@school
def student
@@school ||= Factory(:student, :school => school)
end
end
Otros consejos
¿Qué tipo de pruebas está tratando de escribir? Si realmente quiere asegurarse de que todos estos objetos están coordinando adecuadamente, estás escribiendo una prueba de integración y la velocidad no es su principal preocupación. Sin embargo, si usted está tratando de probar la unidad del modelo, que podría lograr mejores resultados mediante la stubbing agresiva.
Por ejemplo, si usted está tratando de comprobar que un examen utiliza el nombre de su asociación escolar cuando se llama exam.location (o lo que sea que estés llamándolo), que no es necesario un objeto de toda la escuela. Sólo tiene que asegurarse de que el examen está llamando el método correcto en la escuela. Para probar esto, usted podría hacer algo como lo siguiente (utilizando Test :: Unidad y Mocha, porque eso es lo que estoy familiarizado):
test "exam gets location from school name" do
school = stub_everything
school.expects(:name).returns(:a_school_name)
exam = Factory(:exam, :school => school)
assert_equal :a_school_name, exam.location
end
Básicamente, si usted necesita para acelerar las pruebas unitarias porque los objetos son demasiado caros de construir, usted no está realmente la unidad de pruebas. Todos los casos de prueba anteriores se sienten como si debería estar en el nivel de prueba de unidad, de modo talón talón de recibo!
http: // m .onkey.org / 2009/9/20 / hacer tus shoulda-tests-rápido-con-fast_context es un excelente post sobre cómo hacer sus pruebas shoulda / fábrica-chica más rápido, utilizando una gema llamada fast_context . Que me haga saber si no es lo que necesita.
No es un plugin llamado fast_context ( github enlace ) que combina deben declaraciones en un único contexto, la aceleración de las pruebas .
La otra cosa que he estado utilizando para acelerar mis pruebas es rellenando previamente los datos del accesorio. Factorygirl es lento porque está creando esos registros cada vez que el bloque de configuración ejecuta.
escribí un plugin llamado Fixie que utiliza ActiveRecord para rellenar previamente la base de datos de prueba, por lo que la registros que necesita para sus pruebas ya están creados. Se puede utilizar junto con Fixie factorygirl si desea crear nuevos registros en tiempo de ejecución, también.