How can I validate exits and aborts in RSpec?

前端 未结 7 858
傲寒
傲寒 2020-12-04 23:57

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

相关标签:
7条回答
  • 2020-12-05 00:05

    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 讨论(0)
  • 2020-12-05 00:05

    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
    
    0 讨论(0)
  • 2020-12-05 00:11

    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 讨论(0)
  • 2020-12-05 00:16

    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.

    0 讨论(0)
  • 2020-12-05 00:17

    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
    
    0 讨论(0)
  • 2020-12-05 00:25

    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

    0 讨论(0)
提交回复
热议问题