with tutorial tests test run rails how ruby-on-rails ruby-on-rails-3 rspec mocking stubbing

ruby-on-rails - tutorial - rspec with rails



¿Cómo debo aplastar un método globalmente utilizando RSpec? (6)

¿Qué versión de RSpec estás usando? Creo que allow_any_instance_of se introdujo en RSpec 2.14. Para versiones anteriores, puede utilizar:

MyModel.any_instance.stub(:my_method).and_return(false)

Estoy trabajando en una aplicación de Rails. Estoy tratando de aplastar un método globalmente.

Lo que estoy haciendo es colocarlo dentro de la configuración RSpec, en un bloque before(:suite) siguiente manera:

RSpec.configure do |config| config.before(:suite) do allow_any_instance_of(MyModel).to receive(:my_method).and_return(false) end end

Sin embargo, el inicio de la prueba falla con el siguiente error:

in `method_missing'': undefined method `allow_any_instance_of'' for #<RSpec::Core::ExampleGroup:0x00000008d6be08> (NoMethodError)

¿Cualquier pista? ¿Cómo debo aplastar un método globalmente utilizando RSpec?

pag.


Hace poco me encontré con un caso en el que tenía que apilar algo en un bloque before(:all) o before(:context) y encontré que las soluciones aquí no funcionan para mi caso de uso.

La documentación de RSpec antes de () y después () engancha dice que no es compatible:

Los ganchos antes y después se pueden definir directamente en los grupos de ejemplo en los que deberían ejecutarse, o en un bloque global RSpec.configure.

ADVERTENCIA: la configuración de las variables de instancia no se admite en antes (: suite).

ADVERTENCIA: Las simulaciones solo se admiten en antes (: ejemplo).

Nota: los ámbitos: ejemplo y contexto también están disponibles como: each y: all, respectivamente. Usa lo que prefieras.

Problema

Estaba haciendo una gema para escribir un formato de archivo binario que contenía en la marca de tiempo de época de Unix dentro de su encabezado binario. Quería escribir las pruebas de RSpec para verificar la exactitud del encabezado del archivo de salida, y compararlo con un archivo de referencia binario del dispositivo de prueba. Para crear pruebas rápidas, necesitaba escribir el archivo una vez antes de que se ejecutaran todos los bloques de grupo de ejemplo. Para Time.now() la marca de tiempo con el archivo de referencia, tuve que forzar a Time.now() a devolver un valor constante. Esto me llevó por el camino de tratar de Time.now para devolver mi valor objetivo.

Sin embargo, dado que rspec/mocks no admite el apéndice dentro de un bloque before(:all) o before(:context) , no funcionó. Escribir el archivo before(:each) causó otros problemas extraños.

Afortunadamente, me topé con el problema # 240 de rspec-mocks que tenía la solución!

Solución

Desde el 9 de enero de 2014 ( rspec-mocks PR # 519 ) RSpec ahora contiene un método para solucionar esto:

RSpec::Mocks.with_temporary_scope

Ejemplo

require ''spec_helper'' require ''rspec/mocks'' describe ''LZOP::File'' do before(:all) { @expected_lzop_magic = [ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a ] @uncompressed_file_data = "Hello World/n" * 100 @filename = ''lzoptest.lzo'' @test_fixture_path = File.join(File.dirname(__FILE__), ''..'', ''fixtures'', @filename + ''.3'') @lzop_test_fixture_file_data = File.open( @test_fixture_path, ''rb'').read @tmp_filename = File.basename(@filename) @tmp_file_path = File.join( '''', ''tmp'', @tmp_filename) # Stub calls to Time.now() with our fake mtime value so the mtime_low test against our test fixture works # This is the mtime for when the original uncompressed test fixture file was created @time_now = Time.at(0x544abd86) } context ''when given a filename, no options and writing uncompressed test data'' do describe ''the output binary file'' do before(:all) { RSpec::Mocks.with_temporary_scope do allow(Time).to receive(:now).and_return(@time_now) # puts "TIME IS: #{Time.now}" # puts "TIME IS: #{Time.now.to_i}" my_test_file = LZOP::File.new( @tmp_file_path ) my_test_file.write( @uncompressed_file_data ) @test_file_data = File.open( @tmp_file_path, ''rb'').read end } it ''has the correct magic bits'' do expect( @test_file_data[0..8].unpack(''C*'') ).to eq @expected_lzop_magic end ## [...SNIP...] (Other example blocks here) it ''has the original file mtime in LZO file header'' do # puts "time_now= #{@time_now}" if @test_file_data[17..21].unpack(''L>'').first & LZOP::F_H_FILTER == 0 mtime_low_start_byte=25 mtime_low_end_byte=28 mtime_high_start_byte=29 mtime_high_end_byte=32 else mtime_low_start_byte=29 mtime_low_end_byte=32 mtime_high_start_byte=33 mtime_high_end_byte=36 end # puts "start_byte: #{start_byte}" # puts "end_byte: #{end_byte}" # puts "mtime_low: #{@test_file_data[start_byte..end_byte].unpack(''L>'').first.to_s(16)}" # puts "test mtime: #{@lzop_test_fixture_file_data[start_byte..end_byte].unpack(''L>'').first.to_s(16)}" mtime_low = @test_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack(''L>'').first mtime_high = @test_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack(''L>'').first # The testing timestamp has no high bits, so this test should pass: expect(mtime_low).to eq @time_now.to_i expect(mtime_high).to eq 0 expect(mtime_low).to eq @lzop_test_fixture_file_data[mtime_low_start_byte..mtime_low_end_byte].unpack(''L>'').first expect(mtime_high).to eq @lzop_test_fixture_file_data[mtime_high_start_byte..mtime_high_end_byte].unpack(''L>'').first mtime_fixed = ( mtime_high << 16 << 16 ) | mtime_low # puts "mtime_fixed: #{mtime_fixed}" # puts "mtime_fixed: #{mtime_fixed.to_s(16)}" expect(mtime_fixed).to eq @time_now.to_i end end end end


No apague métodos en before(:suite) porque los stubs se borran después de cada ejemplo, como se indica en el archivo README de rspec-mocks :

Usar before(:each) , no before(:all)

Los apéndices before(:all) no son compatibles. La razón es que todos los apéndices y simulacros se borran después de cada ejemplo, por lo que cualquier apéndice que se establezca before(:all) funcionará en el primer ejemplo que se ejecute en ese grupo, pero no para otros.

En lugar de before(:all) , use before(:each) .

Creo que es por eso que allow_any_instance_of no está disponible en el bloque before(:suite) , pero está disponible en el bloque before(:each) .

Si aún falta el método, tal vez haya configurado rspec-mocks para solo permitir :should sintaxis. allow_any_instance_of se introdujo en RSpec 2.14 con toda la nueva sintaxis de expectativa para las expectativas de mensajes.

Asegúrese de que esta sintaxis esté habilitada inspeccionando el valor de RSpec::Mocks.configuration.syntax . Es una matriz de las sintaxis disponibles en rspec-mocks. Las sintaxis disponibles son :expect y :should .

RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.syntax = [:expect, :should] end end

Una vez configurado correctamente, debería poder utilizar allow_any_instance_of .


Probablemente es un problema de contexto / inicialización. Hacerlo en config.before(:each) debería resolver su problema.


Puede usar lo siguiente para apilar un método ''do_this'' de la clase ''Xyz'':

allow_any_instance_of(Xyz).to receive(:do_this).and_return(:this_is_your_stubbed_output)

Esto apaga la salida a - '': this_is_your_stubbed_output'' desde donde se invoca esta función.

Puede usar el fragmento de código anterior en el bloque anterior (: cada uno) para que esto sea aplicable para todos sus ejemplos de especificaciones.


Si desea que un método particular se comporte de cierta manera para su conjunto de pruebas completo, no hay razón para tratar con los apéndices de RSpec. En su lugar, simplemente puede (re) definir el método para comportarse como quiere en su entorno de prueba:

class MyModel def my_method false end end

Esto podría ir en spec/spec_helper.rb o un archivo similar.