Как специфицировать методы, которые выходят или прерываются

У меня есть метод, который запускается из CLI, который имеет некоторые логические пути, которые явно выходят или прерываются. Я обнаружил, что при написании спецификаций для этого метода RSpec отмечает это как сбоя, потому что выходы являются исключениями. Вот пример с голой костью:

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

Я могу обернуть спецификацию в лямбда с помощью should raise_error(SystemExit), но это игнорирует любые утверждения, которые происходят внутри блока. Чтобы быть ясным: я не тестирую сам выход, а логику, которая происходит до него. Как я могу описать этот тип метода?

Ответ 1

Проще говоря, ваши утверждения за пределами лямбда, например:

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 ==)

Есть ли случаи, когда это не сработает для вас?

EDIT: Я добавил вызов 'exit' внутри лямбда, чтобы передать случай, когда logic_and_exit не выходит.

EDIT2: Еще лучше, просто сделайте это в своем тесте:

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

Ответ 2

Новый ответ для покрытия Rspec 3 ожидает синтаксиса.

Тестирование вывода

Просто чтобы проверить, что вы на самом деле хотите (т.е. вы не проверяете исключение или ответ на значение), то, что было выведено на STDOUT.

Если condition false

it "has a false condition" do
  # NOTE: Set up your condition 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 parameters to make it true
  expect {
    begin cli_method
    rescue SystemExit
    end
  }.to output("Everything okay!").to_stdout
end

Обратите внимание, что output("String").to_... может принимать Regex например.

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

Он также может захватывать от stderr например.

output("GTFO").to_stderr

(Это было бы лучше, если бы вы отправили его для примера OP.)

Тестирование выхода

Вы можете отдельно проверить, что ложное условие также вызывает SystemExit

it "exits when condition is false" do
  # NOTE: Set up your condition 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 parameters to make it true
  expect{cli_method}.not_to raise_error SystemExit
end

Ответ 3

Я могу обернуть спецификацию в лямбда с помощью raise_error (SystemExit), но это игнорирует любые утверждения, которые происходят внутри блока.

Я не вижу различий, устанавливающих тесты внутри или вне лямбда. В любом случае сообщение об ошибке является немного загадочным:

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)>'