如何规范退出或中止的方法

时间:2011-08-11 01:37:31

标签: ruby rspec

我有一个从CLI触发的方法,它有一些明确退出或中止的逻辑路径。我发现在为此方法编写规范时,RSpec将其标记为失败,因为退出是异常。这是一个简单的例子:

def cli_method
  if condition
    puts "Everything's okay!"
  else
    puts "GTFO!"
    exit
  end
end

我可以使用should raise_error(SystemExit)将规范包装在lambda中,但忽略块内发生的任何断言。要明确:我不是在测试退出本身,而是在它之前发生的逻辑。我该如何选择这种方法?

3 个答案:

答案 0 :(得分:6)

只需将断言置于lambda之外,例如:

class Foo
  attr_accessor :result

  def logic_and_exit
    @result = :bad_logic
    exit
  end
end

describe 'Foo#logic_and_exit' do
  before(:each) do
    @foo = Foo.new
  end

  it "should set @foo" do
    lambda { @foo.logic_and_exit; exit }.should raise_error SystemExit
    @foo.result.should == :logics
  end
end

当我运行rspec时,它正确告诉我:

expected: :logics
     got: :bad_logic (using ==)

是否有任何不适合您的情况?

编辑:我在lambda中添加了一个'exit'调用来处理logic_and_exit没有退出的情况。

EDIT2:更好的是,只需在测试中执行此操作:

begin
  @foo.logic_and_exit
rescue SystemExit
end
@foo.result.should == :logics

答案 1 :(得分:6)

覆盖Rspec 3的新答案需要语法。

测试输出

只是为了测试你真正想要的东西(即,你没有测试异常,或者是一个值响应),输出到STDOUT的内容。

condition为假时

it "has a false condition" do
  # NOTE: Set up your condition's parameters to make it false
  expect {
    begin cli_method
    rescue SystemExit
    end
  }.to output("GTFO").to_stdout # or .to_stderr
end

condition为真时

it "has a true condition" do
  # NOTE: Set up your condition's parameters to make it true
  expect {
    begin cli_method
    rescue SystemExit
    end
  }.to output("Everything's okay!").to_stdout
end

请注意,output("String").to_...可以接受Regex例如

output(/^Everything's okay!$/).to_stdout

它也可以从stderr中捕获,例如

output("GTFO").to_stderr

(对于OP的例子,这将是一个更好的发送地点。)

测试退出

您可以单独测试错误条件是否也会引发SystemExit

it "exits when condition is false" do
  # NOTE: Set up your condition's parameters to make it false
  expect{cli_method}.to raise_error SystemExit
end

it "doesn't exit when condition is true" do
  # NOTE: Set up your condition's parameters to make it true
  expect{cli_method}.not_to raise_error SystemExit
end

答案 2 :(得分:2)

  

我可以使用raise_error(SystemExit)将规范包装在lambda中,   但这忽略了在街区内发生的任何断言。

我认为将测试放在lambda内部或外部没有区别。在任何一种情况下,失败消息都有点神秘:

def cli_method(condition)
  if condition
    puts "OK"
  else
    puts "GTFO"
    exit
  end
end

describe "cli_method" do
  context "outside lambda" do
    # passing
    it "writes to STDOUT when condition is false" do
      STDOUT.should_receive(:puts).with("GTFO")
      lambda {
        cli_method(false)
      }.should raise_error(SystemExit)
    end

    # failing
    it "does not write to STDOUT when condition is false" do
      STDOUT.should_not_receive(:puts).with("GTFO")
      lambda {
        cli_method(false)
      }.should raise_error(SystemExit)
    end
  end
  context "inside lambda" do
    # passing
    it "writes to STDOUT when condition is false" do
      lambda {
        STDOUT.should_receive(:puts).with("GTFO")
        cli_method(false)
      }.should raise_error(SystemExit)
    end

    # failing
    it "does not write to STDOUT when condition is false" do
      lambda {
        STDOUT.should_not_receive(:puts).with("GTFO")
        cli_method(false)
      }.should raise_error(SystemExit)
    end
  end
end

 # output
.F.F

Failures:

  1) cli_method outside lambda does not write to STDOUT when condition is false
     Failure/Error: lambda {
       expected SystemExit, got #<RSpec::Mocks::MockExpectationError: (#<IO:0xb28cd8>).puts("GTFO")
           expected: 0 times
           received: 1 time>
     # ./gtfo_spec.rb:23:in `block (3 levels) in <top (required)>'

  2) cli_method inside lambda does not write to STDOUT when condition is false
     Failure/Error: lambda {
       expected SystemExit, got #<RSpec::Mocks::MockExpectationError: (#<IO:0xb28cd8>).puts("GTFO")
           expected: 0 times
           received: 1 time>
     # ./gtfo_spec.rb:39:in `block (3 levels) in <top (required)>'