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
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
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
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
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!
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