我正在尝试为脚本接收的命令行参数指定行为,以确保所有验证都通过。我的一些命令行参数将导致调用abort
或exit
,因为提供的参数缺失或不正确。
我正在尝试这样不起作用的事情:
# 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
exit
方法正在彻底解决阻止RSpec验证测试的问题(我得到“SystemExit:exit”)。
我也试过mock(Kernel)
但是这也没有按照我的意愿工作(我没有看到任何明显的区别,但这可能是因为我不确定如何模拟内核和制作确定我的Something
类中使用了模拟的内核。
答案 0 :(得分:26)
试试这个:
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
答案 1 :(得分:17)
感谢Markus的回答。一旦我掌握了这个线索,我就可以将一个好的匹配器放在一起以备将来使用。
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
要使用此匹配器,请将其添加到库或规范助手中:
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
答案 2 :(得分:13)
使用新的RSpec语法:
expect { code_that_exits }.to raise_error(SystemExit)
如果某些内容被打印到STDOUT并且您想要测试它,则可以执行以下操作:
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
capture_stdout
的定义如下所示
Test output to command line with RSpec
更新:考虑使用RSpec's output
matcher代替capture_stdout
答案 3 :(得分:3)
它不漂亮,但我一直在使用它:
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
答案 4 :(得分:2)
挖掘后,I found 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
答案 5 :(得分:2)
不需要自定义匹配器或救援块,只需:
expect { exit 1 }.to raise_error(SystemExit) do |error|
expect(error.status).to eq(1)
end
我认为这是优越的,因为它是明确而简单的Rspec。
答案 6 :(得分:1)
由于更新的语法要求,我不得不更新@Greg提供的解决方案。
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
答案 7 :(得分:0)
如果您所做的只是测试命令的退出状态代码,您可以执行以下操作:
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
这是有效的,因为 system
会返回:
true
当命令以“0”状态代码退出时(即没有错误)。false
当命令以非 0 状态代码(即错误)退出时。如果您想将 rspec 文档输出中的 system
命令的输出静音,您可以像这样重定向它:
system( "will_exit_with_zero_status_code", [ :out, :err ] => File::NULL )
答案 8 :(得分:0)
我已经使用 @Greg 的解决方案多年了,但我已经对其进行了修改以与 RSpec 3+ 配合使用。
我还对其进行了调整,以便代码现在可以选择检查退出状态,我发现这更加灵活。
expect { ... }.to call_exit
expect { ... }.to call_exit.with(0)
expect { ... }.to_not call_exit
expect { ... }.to_not call_exit.with(0)
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