rspec puts output test - ruby

I'm having trouble understanding how to test for output with puts. I need to know what I need to do in my RSPEC file.
This is my RSPEC file:
require 'game_io'
require 'board'
describe GameIO do
before(:each) do
#gameio = GameIO.new
#board = Board.new
end
context 'welcome_message' do
it 'should display a welcome message' do
test_in = StringIO.new("some test input\n")
test_out = StringIO.new
test_io = GameIO.new(test_in, test_out)
test_io.welcome_message
test_io.game_output.string.should == "Hey, welcome to my game. Get ready to be defeated"
end
end
end
This is the file it is testing against:
class GameIO
attr_reader :game_input, :game_output
def initialize(game_input = $stdin, game_output = $stdout)
#stdin = game_input
#stdout = game_output
end
def welcome_message
output "Hey, welcome to my game. Get ready to be defeated"
end
def output(msg)
#stdout.puts msg
end
def input
#stdin.gets
end
end
NOTE: I updated my RSPEC code to reflect changes I made to my test file given suggestions found elsewhere. To resolve the poblem completly I used the changes suggested by Chris Heald in my main file. Thank you all and thank you Chris.

Your initializer should be:
def initialize(game_input = $stdin, game_output = $stdout)
#game_input = game_input
#game_output = game_output
end
The reason for this is that attr_accessor generates methods like this:
# attr_accessor :game_output
def game_output
#game_output
end
def game_output=(output)
#game_output = output
end
(attr_reader generates only the reader method)
Thus, since you never assign #game_output, your game_output method will always return nil.

Just check you are sending it the message:
#gameio.should_receive(:puts).with("Hey, welcome to my game. Get ready to be defeated")

You could stub puts and print.
Perhaps the most fundamental way is to temporarily reassign STDOUT to a variable, and confirm the variable matches what you expect for output.
And Minitest has must_output as an assertion/spec.
The code is thus:
##
# Fails if stdout or stderr do not output the expected results.
# Pass in nil if you don't care about that streams output. Pass in
# "" if you require it to be silent. Pass in a regexp if you want
# to pattern match.
#
# NOTE: this uses #capture_io, not #capture_subprocess_io.
#
# See also: #assert_silent
def assert_output stdout = nil, stderr = nil
out, err = capture_io do
yield
end
err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
y = send err_msg, stderr, err, "In stderr" if err_msg
x = send out_msg, stdout, out, "In stdout" if out_msg
(!stdout || x) && (!stderr || y)
end

Related

Why does this injection of STDOUT through let cause a segmentation fault in ruby?

Full code example is at https://github.com/compwron/seg_replicator
ruby version ruby-2.0.0-p353 (see .rvmrc in example)
Interestingly, if I use .ruby-version the segfault does not replicate (see commit 2, i.e. the diff at https://github.com/compwron/seg_replicator/commit/955ff64fb8a45ac12cc4361f99b581baaa60fa3e for the change that takes the sample code from hanging-forever to segfaulting)
class Game
def initialize(stdout = STDOUT)
$stdout = stdout
end
def foo
0
end
end
require_relative '../lib/game'
describe Game do
let(:output) do
StringIO.new
$stdout = output
end
let(:g) { Game.new output }
describe 'foo' do
it 'test 1' do
expect(g.foo).to eq 0
end
it 'test 2' do
expect(g.foo).to eq 0
end
end
end

Send messages to a global buffer in Ruby

I know global variables aren't encouraged in Ruby but this is what I have been asked to do so you'll just have to go with me on this. I have a game that outputs messages to the command window successfully through the STDOUT. My task is to modify the class so that the messages are not only displayed to the STDOUT channel but also written to a buffer. This is so when I add an additional Sinatra method to the end of the file, the buffer is displayed in a browser (i.e localhost:4567).
So effectively, with the Sinatra gem called, running the spec.rb from a command window should result in the messages being displayed in the Web server in addition to the command window. But I do not know where to start in terms of outputting my messages to the buffer.
I'm pretty sure there is a very simple answer to this but my knowledge of ruby is not great. My thinking is I need to add a line to each activity concatenating the output of each to the global variable $buffer but how do I do this? Obviously following that, I need to write a Sinatra method that displays the contents of the global variable in the web browser.
Hope that makes sense.
I have two files, spec.rb and gen.rb. This is my code so far:
spec.rb
require "gen.rb"
module ImpossibleMachine
# Input and output constants processed by subprocesses
DOWN_ARROW = 1
UP_ARROW = 2
RIGHT_ARROW = 3
REPEAT_ARROW = 4
END_PROCESS = 5
START_CURRENT = 6
# RSpec Tests
describe Game do
describe "#start The impossible machine game" do
before(:each) do
#process = []
#output = double('output').as_null_object
#game = Game.new(#output)
end
it "sends a welcome message" do
#output.should_receive(:puts).with('Welcome to the Impossible Machine!')
#game.start
end
it "sends a starting message" do
#output.should_receive(:puts).with('Starting game...')
#game.start
end
it "should perform lifts_lever_turns_wheel activity which returns REPEAT_ARROW" do
#output.should_receive(:puts).with("Input: #{UP_ARROW}, Activity: Heave_ho_squeek_squeek")
#process[1] = #game.lifts_lever_turns_wheel(UP_ARROW)
#process[1].should == REPEAT_ARROW
end
it "should perform turns_tap_on_pulls_down_seesaw activity which returns DOWN_ARROW" do
#output.should_receive(:puts).with("Input: #{REPEAT_ARROW}, Activity: Drip_drip_creek_creek")
#process[2] = #game.turns_tap_on_pulls_down_seesaw(REPEAT_ARROW)
#process[2].should == DOWN_ARROW
end
it "should perform pulls_down_seezaw_starts_current activity which returns START_CURRENT" do
#output.should_receive(:puts).with("Input: #{DOWN_ARROW}, Activity: Creek_creek_buzz_buzz")
#process[2] = #game.pulls_down_seezaw_starts_current(DOWN_ARROW)
#process[2].should == START_CURRENT
end
it "should perform starts_current_pushes_grove activity which returns RIGHT_ARROW" do
#output.should_receive(:puts).with("Input: #{START_CURRENT}, Activity: Buzz_buzz_pow_wallop")
#process[3] = #game.starts_current_pushes_grove(START_CURRENT)
#process[3].should == RIGHT_ARROW
end
it "sends a finishing message" do
#output.should_receive(:puts).with('...Game finished.')
#game.finish
end
end
end
end
gen.rb
require 'sinatra'
$buffer = ""
# Main class module
module ImpossibleMachine
# Input and output constants processed by subprocesses. MUST NOT change.
DOWN_ARROW = 1
UP_ARROW = 2
RIGHT_ARROW = 3
REPEAT_ARROW = 4
END_PROCESS = 5
START_CURRENT = 6
class Game
attr_reader :process, :output
attr_writer :process, :output
def initialize(output)
#output = output
puts "[#{#output}]"
end
# All the code/methods aimed at passing the RSpect tests are below.
def start
#output.puts'Welcome to the Impossible Machine!'
#output.puts'Starting game...'
end
def lifts_lever_turns_wheel(input)
#input = input
#output.puts 'Input: 2, Activity: Heave_ho_squeek_squeek'
return REPEAT_ARROW
end
def turns_tap_on_pulls_down_seesaw(input)
#input = input
#output.puts 'Input: 4, Activity: Drip_drip_creek_creek'
return DOWN_ARROW
end
def pulls_down_seezaw_starts_current(input)
#input = input
#output.puts 'Input: 1, Activity: Creek_creek_buzz_buzz'
return START_CURRENT
end
def starts_current_pushes_grove(input)
#input = input
#output.puts 'Input: 6, Activity: Buzz_buzz_pow_wallop'
return RIGHT_ARROW
end
def finish
#output.puts'...Game finished.'
end
end
end
# Main program
module ImpossibleMachine
#process = []
g = Game.new(STDOUT)
# All code added to output the activity messages to the command line window is below.
g.start
#process[0] = g.lifts_lever_turns_wheel(2)
#process[1] = g.turns_tap_on_pulls_down_seesaw(#process[0])
#process[2] = g.pulls_down_seezaw_starts_current(#process[1])
#process[3] = g.starts_current_pushes_grove(#process[2])
g.finish
end
# Any sinatra code added to output the activity messages to a browser should be added below.
# End program
managed to get this to work after hours!
At the top add/adjust to:
require 'stringio'
$buffer= StringIO.new
in the main program:
g = Game.new($buffer)
g.start
#process[0] = g.lifts_lever_turns_wheel(2)
etc......
g.finish
puts $buffer.string #this sends it to stdout
then just add in the sinatra coding at the bottom which will also use $buffer.string
possibly not the best or smartest way to do it but it uses the global buffer they wanted and gets it to sinatra and the cmd line.
I think you could just create some sort of buffer object in your main program and pass it in the game, just like you pass in STDOUT. Then you could call write methods on the passed in object inside the game.

Testing STDOUT output in Rspec

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)?"
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
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.

Test output to command line with 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
Can someone point me in the right direction please?
Here's a pretty good way to do this. Copied from the hirb test_helper source:
def capture_stdout(&block)
original_stdout = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = original_stdout
end
fake.string
end
Use like this:
output = capture_stdout { Hello.new.speak }
output.should == "Hello from RSpec\n"
The quietly command is probably what you want (cooked into ActiveSupport, see docs at api.rubyonrails.org). This snippet of RSpec code below shows how to ensure there is no output on stderr while simultaneously silencing stdout.
quietly do # silence everything
commands.each do |c|
content = capture(:stderr) { # capture anything sent to :stderr
MyGem::Cli.start(c)
}
expect(content).to be_empty, "#{c.inspect} had output on stderr: #{content}"
end
end
So you don't have to change your main ruby code I just found out you can do something like this:
def my_meth
print 'Entering my method'
p 5 * 50
puts 'Second inside message'
end
describe '#my_meth' do
it 'puts a 2nd message to the console' do
expect{my_meth}.to output(/Second inside message/).to_stdout
end
end
When checking for a desired output text I used it inside / / like a Regexp because after many many maaany tests and looking around, the STDOUT is everything that is outputted so I found it to be better to use Regex so you could check the whole STDOUT for the exact text that you want.
Like I put it, it works in the terminal just perfect.
//Just had to share this, it took me days to figure it out.
it "should say 'Hello from Rspec' when run" do
output = `ruby sayhello.rb`
output.should == 'Hello from RSpec'
end

How can I copy STDOUT to a file without stopping it showing onscreen using Ruby

I'm attempting to copy stdout to a file for logging purposes. I also want it to display in the Ruby console of the IDE I'm using.
I inserted this code into my script and it redirects $stdout to the my.log file:
$stdout.reopen("my.log", "w")
Does anyone know of a gem or technique to copy the contents of $stdout to a file and not redirect it to a file? Also, I am not using Rails just Ruby.
Something like this might help you:
class TeeIO < IO
def initialize orig, file
#orig = orig
#file = file
end
def write string
#file.write string
#orig.write string
end
end
Most of the methods in IO that do output ultimately use write, so you only have to override this one method. You can use it like this:
#setup
tee = TeeIO.new $stdout, File.new('out.txt', 'w')
$stdout = tee
# Now lots of example uses:
puts "Hello"
$stdout.puts "Extending IO allows us to expicitly use $stdout"
print "heres", :an, :example, "using", 'print', "\n"
48.upto(57) do |i|
putc i
end
putc 10 #newline
printf "%s works as well - %d\n", "printf", 42
$stdout.write "Goodbye\n"
After this example, the following is written identically to both the console and to the file:
Hello
Extending IO allows us to expicitly use $stdout
heresanexampleusingprint
0123456789
printf works as well - 42
Goodbye
I won't claim this technique is fail proof, but it should work for simple uses of stdout. Test it for your use.
Note that you don't have to use reopen on $stdout unless you want to redirect output from a child process or an uncooperative extension. Simply assigning a different IO object to it will work for most uses.
RSpec
The RSpec command line takes a reference to $stdout before you can get any code to run to reassign it, so this doesn't work. reopen still works in this case as you're changing the actual object pointed to by both $stdout and the reference that RSpec has, but this doesn't give you output to both.
One solution is to monkey-patch $stdout like this:
$out_file = File.new('out.txt', 'w')
def $stdout.write string
$out_file.write string
super
end
This works, but as with all monkey patching, be careful. It would be safer to use your OS's tee command.
If you are using Linux or Mac OS, the tee command available in the OS makes it easy to do this. From its man page:
NAME
tee -- pipe fitting
SYNOPSIS
tee [-ai] [file ...]
DESCRIPTION
The tee utility copies standard input to standard output, making a copy in zero or more files. The output is unbuffered.
So something like:
echo '>foo bar' | tee tmp.out
>foo bar
echos the output to STDOUT and to the file. Catting the file gives me:
cat tmp.out
>foo bar
Otherwise, if you want to do it inside your code, it's a simple task:
def tee_output(logfile)
log_output = File.open(logfile, 'w+')
->(o) {
log_output.puts o
puts o
}
end
tee = tee_output('tmp.out')
tee.call('foo bar')
Running it:
>ruby test.rb
foo bar
And checking the output file:
>cat tmp.out
foo bar
I'd use "w+" for my file access to append to the output file, rather than over-write it.
CAVEAT: This opens the file and leaves it open during the life of the code after you've called the tee_output method. That bothers some people, but, personally, it doesn't bother me because Ruby will close the file when the script exits. In general we want to close files as soon as we're done with them, but in your code, it makes more sense to open it and leave it open, than to repeatedly open and close the output file, but your mileage might vary.
EDIT:
For Ruby 1.8.7, use lambda instead of the new -> syntax:
def tee_output(logfile)
log_output = File.open(logfile, 'w+')
lambda { |o|
log_output.puts o
puts o
}
end
tee = tee_output('tmp.out')
tee.call('foo bar')
I know it's a old question, but I found myself in the same situation. I wrote a Multi-IO Class which extends File and overrode write puts and close methods, I also made sure its thread safe:
require 'singleton'
class MultiIO < File
include Singleton
##targets = []
##mutex = Mutex.new
def self.instance
self.open('/dev/null','w+')
end
def puts(str)
write "#{str}\n"
end
def write(str)
##mutex.synchronize do
##targets.each { |t| t.write str; flush }
end
end
def setTargets(targets)
raise 'setTargets is a one-off operation' unless ##targets.length < 1
targets.each do |t|
##targets.push STDOUT.clone if t == STDOUT
##targets.push STDERR.clone if t == STDERR
break if t == STDOUT or t == STDERR
##targets.push(File.open(t,'w+'))
end
self
end
def close
##targets.each {|t| f.close}
end
end
STDOUT.reopen MultiIO.instance.setTargets(['/tmp/1.log',STDOUT,STDERR])
STDERR.reopen STDOUT
threads = []
5.times.each do |i|
threads.push(
Thread.new do
10000.times.each do |j|
STDOUT.puts "out#{i}:#{j}"
end
end
)
end
5.times.each do |i|
threads.push(
Thread.new do
10000.times.each do |j|
STDERR.puts "err#{i}:#{j}"
end
end
)
end
threads.each {|t| t.join}
Tiny improvement from matts answer:
class TeeIO < IO
def initialize(ios)
#ios = ios
end
def write(string)
#ios.each { |io| io.write string }
end
end

Resources