[ACCEPTED]-How can I validate exits and aborts in RSpec?-mocking
try this:
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
Using the new RSpec syntax:
expect { code_that_exits }.to raise_error(SystemExit)
If something 4 is printed to STDOUT and you want to test 3 that too, you can do something like:
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
Where 2 capture_stdout
is defined as seen in
Test output to command line with RSpec.
Update: Consider using 1 RSpec's output
matcher instead of capture_stdout
Thanks for the answer Markus. Once I had 3 this clue I could put together a nice matcher 2 for future use.
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
To use this matcher add this 1 to your libraries or spec-helpers:
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
There's no need for custom matchers or rescue 2 blocks, simply:
expect { exit 1 }.to raise_error(SystemExit) do |error|
expect(error.status).to eq(1)
end
I'd argue that this is superior 1 because it's explicit and plain Rspec.
Its not pretty, but I've been using this:
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
0
After digging, I found this.
My solution ended up looking 1 like this:
# 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
I had to update the solution @Greg provided 1 due to newer syntax requirements.
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
Just looking for exit status code of a command.
If all you're doing is testing the exit 5 status code of a command, you can do something 4 like this:
describe Something do
it "should exit without an error" do
expect( system( "will_exit_with_zero_status_code" ) ).to be true
end
it "should exit with an error" do
expect( system( "will_exit_with_non_zero_status_code" ) ).to be false
end
end
This works because system
will return:
true
when the command exits with a "0" status code (i.e. no errors).false
when the command exits with a non 0 status code (i.e. error).
And 3 if you want to mute the output of the system
command 2 from your rspec documentation output, you 1 can redirect it like so:
system( "will_exit_with_zero_status_code", [ :out, :err ] => File::NULL )
I've used @Greg's solution for years now, but 4 I've modified it to work with RSpec 3+.
I've 3 also tweaked it so that the code now optionally 2 checks for an exit status, which I find 1 much more flexible.
Usage
expect { ... }.to call_exit
expect { ... }.to call_exit.with(0)
expect { ... }.to_not call_exit
expect { ... }.to_not call_exit.with(0)
Source
RSpec::Matchers.define :call_exit do
actual_status = nil
match do |block|
begin
block.call
rescue SystemExit => e
actual_status = e.status
end
actual_status && (expected_status.nil? || actual_status == expected_status)
end
chain :with, :expected_status
def supports_block_expectations?
true
end
failure_message do |block|
expected = 'exit'
expected += "(#{expected_status})" if expected_status
actual = nil
actual = "exit(#{actual_status})" if actual_status
"expected block to call `#{expected}` but " +
(actual.nil? ? 'exit was never called' : "`#{actual}` was called")
end
failure_message_when_negated do |block|
expected = 'exit'
expected += "(#{expected_status})" if expected_status
"expected block not to call `#{expected}`"
end
description do
expected = 'exit'
expected += "(#{expected_status})" if expected_status
"expect block to call `#{expected}`"
end
end
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.