I am trying to spec behaviors for command line arguments my script receives to ensure that all validation passes. Some of my command line arguments will result in abo
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
Thanks for the answer Markus. Once I had this clue I could put together a nice matcher 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 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
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
There's no need for custom matchers or rescue 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 because it's explicit and plain Rspec.
I had to update the solution @Greg provided 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
Using the new RSpec syntax:
expect { code_that_exits }.to raise_error(SystemExit)
If something is printed to STDOUT and you want to test 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 capture_stdout
is defined as seen in
Test output to command line with RSpec.
Update: Consider using RSpec's output matcher instead of capture_stdout