ruby-on-rails - matchers - rspec rails tutorial
Prueba del Rake en Rails: aumento de errores mĂșltiples silenciados en prueba (3)
¿Intentó pasar el error específico ?:
expect { Rake::Task[task_name].invoke }.to raise_error(StandardError)
Tengo una tarea de rastrillo que protege contra peligrosas rasks de rastrillo Rails, en función del entorno. Funciona bien. Cuando pruebo cada método peligroso individual en RSpec, la prueba pasa. Cuando pruebo múltiples en una fila, para múltiples entornos, la prueba falla después de la primera. Incluso si ejecuto la prueba varias veces por la misma acción peligrosa, rake db:setup
por ejemplo, solo pasará la primera vez. Si ejecuto las pruebas como declaraciones individuales, una para cada acción peligrosa, solo las dos primeras pasarán (hay 4).
¿Cómo puedo hacer que RSpec se comporte correctamente aquí, y pasar todas las pruebas cuando se ejecuta en una suite?
La tarea de rake
# guard_dangerous_tasks.rake
class InvalidTaskError < StandardError; end
task :guard_dangerous_tasks => :environment do
unless Rails.env == ''development''
raise InvalidTaskError
end
end
%w[ db:setup db:reset ].each do |task|
Rake::Task[task].enhance [''guard_dangerous_tasks'']
end
La prueba RSpec
require ''spec_helper''
require ''rake''
load ''Rakefile''
describe ''dangerous_tasks'' do
context ''given a production environment'' do
it ''prevents dangerous tasks'' do
allow(Rails).to receive(:env).and_return(''production'')
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
end
context ''given a test environment'' do
it ''prevents dangerous tasks'' do
allow(Rails).to receive(:env).and_return(''test'')
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
end
end
Salida RSpec
# we know the guard task did its job,
# because the rake task didn''t actually run.
Failure/Error: expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
expected InvalidTaskError but nothing was raised
Parece que dos tareas no pueden apuntar a la misma tarea para la enhancement
por lo que tal vez haya un conflicto en el tiempo de ejecución. Entonces prueba el método de block
para manejar la situación.
class InvalidTaskError < StandardError; end
%w[ db:setup db:reset ].each do |task|
Rake::Task[task].enhance do
unless Rails.env == ''development''
raise InvalidTaskError
end
end
end
y en el archivo de especificaciones, la siguiente modificación crearía dos ejemplos para rastrear las especificaciones de manera adecuada.
# require ''rails_helper''
require ''spec_helper''
require ''rake''
load ''Rakefile''
describe ''dangerous_tasks'' do
context ''given a production environment'' do
%w[ db:setup db:reset ].each do |task_name|
it "prevents dangerous tasks #{task_name}" do
allow(Rails).to receive(:env).and_return(''production'')
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
end
end
Puedo pensar en dos soluciones para tu problema.
Pero primero debemos averiguar dónde está la raíz del problema.
Raíz del problema
Comencemos con una línea de su código:
Rake::Task[task].enhance [''guard_dangerous_tasks'']
Comparándolo con el código fuente de Rake::Task
# File rake/task.rb, line 96
def enhance(deps=nil, &block)
@prerequisites |= deps if deps
@actions << block if block_given?
self
end
puede ver que guard_dangerous_tasks
debe agregarse a @prerequisites
array. Se puede verificar fácilmente:
p Rake::Task[''db:reset''].prerequisites # => ["environment", "load_config", "guard_dangerous_tasks"]
Continuando con su código fuente.
Utiliza invoke
para ejecutar tareas. Si prestamos mucha atención para invoke
la documentación de ''s'', indica:
Invoca la tarea si es necesario .
Una vez que se ejecuta la tarea, no se puede invocar nuevamente (a menos que la volvamos a habilitar).
Pero, ¿por qué debería ser esto un problema? Estamos ejecutando diferentes tareas, ¿verdad? ¡Pero en realidad no lo hacemos!
guard_dangerous_tasks
antes de todas las tareas en nuestra matriz de tareas. Y se está ejecutando solo una vez.
La solución n. ° 1 no es la mejor
Tan pronto como sepamos cuál es nuestro problema, podemos pensar en uno (no es la mejor solución).
guard_dangerous_tasks
habilitar guard_dangerous_tasks
después de cada iteración:
dangerous_task = Rake::Task[''guard_dangerous_tasks'']
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
dangerous_task.reenable
end
Solución # 2 guard_dangerous_tasks
no es un requisito previo
¡ guard_dangerous_tasks
una mejor solución a nuestro problema si nos damos cuenta de que guard_dangerous_tasks
no debería ser un requisito previo! Se supone que los requisitos previos para "preparar" la etapa y se ejecuten una sola vez. ¡Pero nunca deberíamos cegar nuestros ojos a los peligros!
Esta es la razón por la que deberíamos extender con guard_dangerous_tasks
como una acción, que se ejecutará cada vez que se ejecute la tarea principal.
De acuerdo con el código fuente de Rake::Task
(ver arriba) deberíamos pasar nuestra lógica en un bloque si queremos que se agregue como una acción.
%w[ db:setup db:reset ].each do |task|
Rake::Task[task].enhance do
Rake::Task[''guard_dangerous_tasks''].execute
end
end
Podemos dejar nuestra prueba sin cambios ahora y pasa:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
Pero dejar invoke
es una entrada para nuevos problemas. Es mejor ser reemplazado por execute
:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
end
¡Ten cuidado con invoke
!
Dijimos más arriba, que usar invoke
es un ticket para nuevos problemas. ¿Qué tipo de problemas?
Probemos probar nuestro código para entornos de test
y production
. Si envolvemos nuestras pruebas dentro de este ciclo:
[''production'',''test''].each do |env_name|
env = ActiveSupport::StringInquirer.new(env_name)
allow(Rails).to receive(:env).and_return(env)
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
nuestra prueba fallará con la razón original. Puedes arreglar esto fácilmente al reemplazar la línea
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
con
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
Entonces, ¿cuál fue el motivo? Probablemente ya lo adivines.
En la prueba de falla, invocamos las mismas dos tareas dos veces. La primera vez que fueron ejecutados. La segunda vez deberían volver a habilitarse antes de la invocación para su ejecución. Cuando usamos execute
, la acción se vuelve a habilitar automáticamente.
Nota Puede encontrar un ejemplo de trabajo de este proyecto aquí: https://github.com/dimakura/-projects/tree/master/31821220-testing-rake