ruby - Generación de especificación de RSpec dinámica
dynamic specifications (2)
Me gustaría generar una serie de especificaciones basadas dinámicamente en una fuente de datos externa. Específicamente, tengo una hoja de cálculo de Google que se descarga, y cada fila en la hoja de cálculo debe usarse para su propio caso de prueba:
describe "Cases" do
before(:all) do
# Download spreadsheet and
# populate cases in DB
end
Cases.each do |case|
it "Case #{case.num}" do
# spec
end
end
end
Esto no funciona, porque, para empezar con RSpec, no (como yo lo entiendo) ''ver'' cualquier especificación en tiempo de compilación, por lo que before(:all)
nunca se ejecuta. Si Cases.each
que colocar un bloque vacío it
obtendría el before(:all)
para ejecutar, pero luego me encontraría con el problema de que Cases.each
se evalúa antes que cualquier otra cosa, que está vacío porque no ha sido poblado aún por el bloque before(:all)
.
En resumen, estoy confundido, y mi comprensión de RSpec parece muy limitada. Me gustaría buscar datos, usar esos datos para configurar un conjunto de especificaciones y luego ejecutarlas mediante RSpec. Esto podría (¿podría?) Funcionar si los Casos fueran una matriz configurada de antemano (fuera del bloque de descripción), pero necesito que se configure en tiempo de ejecución. ¿Es lo que quiero hacer en RSpec?
Retire el bloque before
.
El código en una describe
ejecuta cuando se carga rspec. Tiene la desventaja de que descargará y escaneará la hoja incluso si estas especificaciones no se van a ejecutar.
El siguiente problema con el que tendrás que lidiar es que el db se vacía después de cada especificación. Por lo tanto, deberá rellenar la base de datos en un bloque anterior para cada especificación que se crea a continuación.
Yo enfatizaría que no recomendaría esto, pensaría profundamente en tener los casos de prueba sacados dinámicamente. Considere pruebas genéricas que demuestren que su código funciona sin los datos externos. O saque una copia de la hoja de cálculo, guárdela en spec/assets
y cárguela, con pruebas más directas para ese ejemplo en particular.
describe "Cases" do
# Download spreadsheet and
# populate cases in DB
before(:each) do
# repopulate the DB
end
Cases.each do |case|
it "Case #{case.num}" do
# spec
end
end
# clean out the db so that the first executed test is not polluted
end
Esto no funciona, porque ... RSpec no ... ''ve'' ninguna especificación en tiempo de compilación ...
Me preguntaba por qué e hice algunas investigaciones. Si examina el siguiente código y la salida, puede sacar algunas conclusiones:
- no hay tiempo de compilación en Ruby
- el cuerpo de una clase / módulo se evalúa y ejecuta inmediatamente
- también lo es el método de
describe
- el cuerpo de un bloque de
describe
es procesado inmediatamente por RSpec - RSpec almacena
before
bloques y bloquea para su posterior ejecución - cuando finaliza el análisis sintáctico de un archivo, RSpec comienza a informar las cadenas de
describe
y ejecuta los bloques de ejemplo almacenadosbefore/it
El siguiente código demuestra esto.
module SO
puts ''>>>>> Ruby sees module SO and executes/evaluates its body''
cases = [1,2,3]
describe "SO Cases" do
puts "in module SO, RSpec sees describe Cases self=#{self}"
before(:each) do
puts '' in before(:all)''
end
cases.each do |case_|
puts " in each loop with case_=#{case_}"
it "Case #{case_}" do
puts " check spec for case #{case_}"
end
end
end
end
module M # to avoid "warning: class variable access from toplevel"
puts ''>>>>> Ruby sees module M and executes/evaluates its body''
describe "M Cases" do
puts "in module M, RSpec sees describe Cases self=#{self}, ancestors :"
ancestors.each {|a| puts " #{a}"}
print ''self.methods.grep(/^it/) : ''; p self.methods.grep(/^it/).sort
before(:all) do
puts " in before(:all) self=#{self}"
@@cases = [1,2,3]
puts " ... now cases=#{@@cases}"
File.open(''generated_cases.rb'', ''w'') do |fgen|
fgen.puts ''puts "/n*** inside generated_cases.rb ***"''
fgen.puts ''module GenSpecs''
fgen.puts "puts ''>>>>> Ruby sees module GenSpecs and executes/evaluates its body''"
fgen.puts '' describe "GenSpecs Cases" do''
fgen.puts '' puts "in module GenSpecs, RSpec sees describe Cases"''
@@cases.each do |case_|
puts " in each loop with case_=#{case_}"
fgen.puts <<-IT_SPECS
it "Case #{case_}" do
puts " some spec for case_=#{case_}"
end
IT_SPECS
end
fgen.puts '' end''
fgen.puts ''end # module GenSpecs''
fgen.puts "puts ''>>>>> end of ruby file generated_cases.rb <<<<<''"
end
puts ''file generated_cases.rb has been closed''
require_relative ''generated_cases''
end
it ''has loaded Cases'' do
@@cases.should_not be_empty
end
end
end
puts ''>>>>> end of ruby file t2_spec.rb <<<<<''
Ejecución:
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ rspec --version
2.12.2
$ rspec --format nested t2_spec.rb
>>>>> Ruby sees module SO and executes/evaluates its body
in module SO, RSpec sees describe Cases self=#<Class:0x007fcaf49a6e80>
in each loop with case_=1
in each loop with case_=2
in each loop with case_=3
>>>>> Ruby sees module M and executes/evaluates its body
in module M, RSpec sees describe Cases self=#<Class:0x007fcaf2852e28>, ancestors :
#<Class:0x007fcaf2852e28>
RSpec::Core::ExampleGroup
...
self.methods.grep(/^it/) : [:it, :it_behaves_like, :it_should_behave_like, :its]
>>>>> end of ruby file t2_spec.rb <<<<<
SO Cases
in before(:all)
check spec for case 1
Case 1
in before(:all)
check spec for case 2
Case 2
in before(:all)
check spec for case 3
Case 3
M Cases
in before(:all) self=#<RSpec::Core::ExampleGroup::Nested_2:0x007fcaf2836ca0>
... now cases=[1, 2, 3]
in each loop with case_=1
in each loop with case_=2
in each loop with case_=3
file generated_cases.rb has been closed
*** inside generated_cases.rb ***
>>>>> Ruby sees module GenSpecs and executes/evaluates its body
in module GenSpecs, RSpec sees describe Cases
>>>>> end of ruby file generated_cases.rb <<<<<
has loaded Cases
GenSpecs Cases
some spec for case_=1
Case 1
some spec for case_=2
Case 2
some spec for case_=3
Case 3
Finished in 0.01699 seconds
7 examples, 0 failures
Crear un archivo y requerirlo de esta manera es para la demostración y puede no funcionar en su caso. Recomendaría hacerlo en dos fases: en el primero, lees la hoja de cálculo y creas un archivo .rb con una describe
y varios ejemplos. En la segunda fase, ejecuta Ruby para procesar el archivo generado.