2009-09-26 13 views
36

Estoy tratando de especificar comportamientos para los argumentos de línea de comandos que recibe mi script para garantizar que todas las validaciones pasen. Algunos de mis argumentos en la línea de comandos darán como resultado que se invoquen abort o exit porque faltan los parámetros proporcionados o son incorrectos.¿Cómo puedo validar las salidas y cancelaciones en RSpec?

Estoy intentando algo como esto que no está funcionando:

# something_spec.rb 
require 'something' 
describe Something do 
    before do 
     Kernel.stub!(:exit) 
    end 

    it "should exit cleanly when -h is used" do 
     s = Something.new 
     Kernel.should_receive(:exit) 
     s.process_arguments(["-h"]) 
    end 
end 

El método exit está disparando limpiamente la prevención RSpec de validación de la prueba (recibo "SystemExit: salida").

También he intentado con mock(Kernel) pero eso tampoco está funcionando como me gustaría (no veo ninguna diferencia perceptible, pero eso es probable porque no estoy seguro de cómo exactamente burlarse de Kernel y asegurarme de que el burlado Kernel se usa en mi clase Something).

Respuesta

25

probar esto:

module MyGem 
    describe "CLI" do 
    context "execute" do 

     it "should exit cleanly when -h is used" do 
     argv=["-h"] 
     out = StringIO.new 
     lambda { ::MyGem::CLI.execute(out, argv) }.should raise_error SystemExit 
     end 

    end 
    end 
end 
+0

Advertencia: Hemos tenido problemas con una solución similar debido presumiblemente RSpec 'exit's cuando fallan las expectativas, por lo que podría terminar rescatando' exit's de RSpec en vez de nuestra propia –

2

Después de excavar, I found this.

Mi solución terminó con este aspecto:

# something.rb 
class Something 
    def initialize(kernel=Kernel) 
     @kernel = kernel 
    end 

    def process_arguments(args) 
     @kernel.exit 
    end 
end 

# something_spec.rb 
require 'something' 
describe Something do 
    before :each do 
     @mock_kernel = mock(Kernel) 
     @mock_kernel.stub!(:exit) 
    end 

    it "should exit cleanly" do 
     s = Something.new(@mock_kernel) 
     @mock_kernel.should_receive(:exit) 
     s.process_arguments(["-h"]) 
    end 
end 
16

Gracias por la respuesta Markus. Una vez que tuve esta pista, pude armar un buen comparador para uso futuro.

it "should exit cleanly when -h is used" do 
    lambda { ::MyGem::CLI.execute(StringIO.new, ["-h"]) }.should exit_with_code(0) 
end 
it "should exit with error on unknown option" do 
    lambda { ::MyGem::CLI.execute(StringIO.new, ["--bad-option"]) }.should exit_with_code(-1) 
end 

Para utilizar esta matcher Agregar a su bibliotecas o Spec-ayudantes:

RSpec::Matchers.define :exit_with_code do |exp_code| 
    actual = nil 
    match do |block| 
    begin 
     block.call 
    rescue SystemExit => e 
     actual = e.status 
    end 
    actual and actual == exp_code 
    end 
    failure_message_for_should do |block| 
    "expected block to call exit(#{exp_code}) but exit" + 
     (actual.nil? ? " not called" : "(#{actual}) was called") 
    end 
    failure_message_for_should_not do |block| 
    "expected block not to call exit(#{exp_code})" 
    end 
    description do 
    "expect block to call exit(#{exp_code})" 
    end 
end 
3

No es bonita, pero yo he estado usando esto:

begin 
    do_something 
rescue SystemExit => e 
    expect(e.status).to eq 1 # exited with failure status 
    # or 
    expect(e.status).to eq 0 # exited with success status 
else 
    expect(true).eq false # this should never happen 
end 
12

Usando el nuevo Sintaxis RSpec:

expect { code_that_exits }.to raise_error(SystemExit) 

If som ething se imprime en la salida estándar y desea probar que también se puede hacer algo como:

context "when -h or --help option used" do 
    it "prints the help and exits" do 
    help = %Q(
     Usage: my_app [options] 
     -h, --help      Shows this help message 
    ) 

    ARGV << "-h" 
    expect do 
     output = capture_stdout { my_app.execute(ARGV) } 
     expect(output).to eq(help) 
    end.to raise_error(SystemExit) 

    ARGV << "--help" 
    expect do 
     output = capture_stdout { my_app.execute(ARGV) } 
     expect(output).to eq(help) 
    end.to raise_error(SystemExit) 
    end 
end 

Dónde capture_stdout se define como se ve en Test output to command line with RSpec.

Actualización: Considere el uso de RSpec's output matcher en lugar de capture_stdout

+4

etc. También hay un built-in 'output' matcher: https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher –

+0

@JaredBeck gracias por el comentario. Hace poco utilicé el parche 'output_to_stdout_from_any_process' de RSpec para capturar la salida de un comando del sistema y, aunque es lento debido a la E/S del disco, funcionó bien. – Dennis

1

que tenía que actualizar la solución @ Greg proporcionado debido a los requisitos de sintaxis más nuevos.

RSpec::Matchers.define :exit_with_code do |exp_code| 
    actual = nil 
    match do |block| 
    begin 
     block.call 
    rescue SystemExit => e 
     actual = e.status 
    end 
    actual and actual == exp_code 
    end 
    failure_message do |block| 
    "expected block to call exit(#{exp_code}) but exit" + 
     (actual.nil? ? " not called" : "(#{actual}) was called") 
    end 
    failure_message_when_negated do |block| 
    "expected block not to call exit(#{exp_code})" 
    end 
    description do 
    "expect block to call exit(#{exp_code})" 
    end 
    supports_block_expectations 
end 
Cuestiones relacionadas