Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test puts in rspec

Tags:

rspec

I want to do is run ruby sayhello.rb on the command line, then receive Hello from Rspec.

I've got that with this:

class Hello
  def speak
    puts 'Hello from RSpec'
  end
end

hi = Hello.new #brings my object into existence
hi.speak

Now I want to write a test in rspec to check that the command line output is in fact "Hello from RSpec" and not "I like Unix"

NOT WORKING. I currently have this in my sayhello_spec.rb file

require_relative 'sayhello.rb' #points to file so I can 'see' it

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    STDOUT.should_receive(:puts).with('Hello from RSpec')    
  end
end

Also, I need to actually see what the test should look like in my RSPEC please.

like image 428
Jessi Avatar asked Jul 17 '13 20:07

Jessi


5 Answers

I think the best way is to use rspec build in output matcher https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher

Fore example, this is your class

class MakeIt   def awesome(text)     puts "Awesome #{text}"   end end 

and your test

describe MakeIt do   describe '#awesome' do     it 'prints awesome things' do       expect do         MakeIt.new.awesome('tests')       end.to output('Awesome tests').to_stdout     end      it 'does not print not awesome things' do       expect do         MakeIt.new.awesome('tests')       end.to_not output('Not awesome tests').to_stdout     end   end end 

Nice, clean and by the book!

like image 168
Artur Małecki Avatar answered Sep 22 '22 14:09

Artur Małecki


Based on previous answers/comments, a solution using the new syntax without a gem would look like this:

describe "sayhello.rb" do   it "should say 'Hello from Rspec' when run" do             expect(STDOUT).to receive(:puts).with('Hello from RSpec')     require_relative 'sayhello.rb'  # load/run the file    end end 
like image 45
Dan Avatar answered Sep 24 '22 14:09

Dan


You're executing your code before entering the test block, so the expectations are not being met. You need to run the code within the test block after setting expectations (e.g. by moving the require_relative statement after the STDOUT.... statement), as follows:

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    STDOUT.should_receive(:puts).with('Hello from RSpec')
    require_relative 'sayhello.rb' #load/run the file 
  end
end
like image 43
Peter Alfvin Avatar answered Sep 21 '22 14:09

Peter Alfvin


You can solve this using Rails' active_support library, which adds a capture method:

require 'active_support/core_ext/kernel/reporting'
require_relative 'sayhello'

describe Hello do
  it "says 'Hello from RSpec' when ran" do
    output = capture(:stdout) do
      hi = Hello.new
      hi.speak
    end
    expect(output).to include 'Hello from RSpec'
  end
end
like image 36
bswinnerton Avatar answered Sep 24 '22 14:09

bswinnerton


Somewhat similar to bswinnerton's answer, one can capture puts output and then test against the captured output, without having to use the library-dependent capture method (which someone has mentioned is being deprecated in Rails 5).

Ruby has a global variable named $stdout which by default is populated by the constant STDOUT. STDOUT is that which sends data to the ruby process's stdout stream (not sure if "stream" is the right term here). Basically in a naive case STDOUT.puts("foo") will result in "foo\n" appearing in your terminal window. $stdout.puts("foo") will do the same thing because the $stdout variable name refers to STDOUT unless you reassign it (key point here). Finally puts("foo") is syntactic sugar for $stdout.puts("foo").

The strategy then is to reassign $stdout to a local IO instance which you can inspect after running your code, to see if "Hello from RSpec" showed up in its contents.

How this would work:

describe "sayhello.rb" do
  it "should say 'Hello from Rspec' when ran" do        
    $stdout = StringIO.new

    # run the code
    # (a little funky; would prefer Hello.new.speak here but only changing one thing at a time)
    require_relative 'sayhello.rb' 

    $stdout.rewind   # IOs act like a tape so we gotta rewind before we play it back  

    expect($stdout.gets.strip).to eq('Hello from Rspec')
  end
end
like image 34
Luke Griffiths Avatar answered Sep 25 '22 14:09

Luke Griffiths