ruby dynamic rspec specifications

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 almacenados before/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.