Testing STDOUT output in Rspec

前端 未结 2 924
北恋
北恋 2020-12-08 02:25

I am trying to build a spec for this statement. It is easy with \'puts\'

print \"\'#{@file}\' doesn\'t exist: Create Empty File (y/n)?\"
相关标签:
2条回答
  • 2020-12-08 02:46

    RSpec 3.0+

    RSpec 3.0 added a new output matcher for this purpose:

    expect { my_method }.to output("my message").to_stdout
    expect { my_method }.to output("my error").to_stderr
    

    Minitest

    Minitest also has something called capture_io:

    out, err = capture_io do
      my_method
    end
    
    assert_equals "my message", out
    assert_equals "my error", err
    

    RSpec < 3.0 (and others)

    For RSpec < 3.0 and other frameworks, you can use the following helper. This will allow you to capture whatever is sent to stdout and stderr, respectively:

    require 'stringio'
    
    def capture_stdout(&blk)
      old = $stdout
      $stdout = fake = StringIO.new
      blk.call
      fake.string
    ensure
      $stdout = old
    end
    
    def capture_stderr(&blk)
      old = $stderr
      $stderr = fake = StringIO.new
      blk.call
      fake.string
    ensure
      $stderr = old
    end
    

    Now, when you have a method that should print something to the console

    def my_method
      # ...
      print "my message"
    end
    

    you can write a spec like this:

    it 'should print "my message"' do
      printed = capture_stdout do
        my_method # do your actual method call
      end
    
      printed.should eq("my message")
    end
    
    0 讨论(0)
  • 2020-12-08 02:52

    If your goal is only to be able to test this method, I would do it like this:

    class Executable
      def initialize(outstream, instream, file)
        @outstream, @instream, @file = outstream, instream, file
      end
    
      def prompt_create_file
        @outstream.print "'#{@file}' doesn't exist: Create Empty File (y/n)?"
      end
    end
    
    
    # when executing for real, you would do something like
    # Executable.new $stdout, $stdin, ARGV[0]
    
    # when testing, you would do
    describe 'Executable' do
      before { @input = '' }
      let(:instream)   { StringIO.new @input }
      let(:outstream)  { StringIO.new }
      let(:filename)   { File.expand_path '../testfile', __FILE__ }
      let(:executable) { Executable.new outstream, instream, filename }
    
      specify 'prompt_create_file prompts the user to create a new file' do
        executable.prompt_create_file
        outstream.string.should include "Create Empty File (y/n)"
      end
    end
    

    However, I want to point out that I would not test a method like this directly. Instead, I'd test the code that uses it. I was talking with a potential apprentice yesterday, and he was doing something very similar, so I sat down with him, and we reimplemented a portion of the class, you can see that here.

    I also have a blog that talks about this kind of thing.

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