How do you capture multiple lines of console output in Ruby? Here is one way to do it. This example comes from writing a test for multiple lines of output from a command-line tic-tac-toe game, where the board should print into the console on 5 different lines.
Solution
describe "#print_board" do
it "prints current state of gameboard" do
game.board = ["O", "X", "O", "X", "O", "X", "O", "X", "O"]
$stdout = StringIO.new
game.print_board
$stdout.rewind
expect($stdout.gets).to eq(" O | X | O \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" X | O | X \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" O | X | O \n")
end
end
$stdout - The current standard output
STDOUT - The standard output. It is the default value for $stdout
StringIO - A class that is a "psuedo I/O on a string object."
What does any of this actually mean? Let's help Ruby-Doc.org out a bit
$stdout - It's where the output currently goes. It can be reassigned to something else. If you reassign it to a file or a StringIO object, the output is now going there.
STDOUT - This is where output goes at the time a Ruby process was launched. It is the default value for $stdout and unless $stdout is reassigned they are essentially the same. This is good because you can reassign $stdout to STDOUT to undo any reassignment you have made.
StringIO - You can create a StringIO object from this class and do a number of things with it including write puts to it, or gets strings from it
Solution breakdown
The entire solution again:
describe "#print_board" do
it "prints current state of gameboard" do
game.board = ["O", "X", "O", "X", "O", "X", "O", "X", "O"]
$stdout = StringIO.new
game.print_board
$stdout.rewind
expect($stdout.gets).to eq(" O | X | O \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" X | O | X \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" O | X | O \n")
end
end
Code breakdown
$stdout = StringIO.new
Assigns $stdout to a new StringIO object. Any console output is now being saved to a new StringIO object that $stdout points to.
game.print_board
This prints the board to the console.
$stdout.rewind
This calls a .rewind method available from the StringIO class (of which $stdout is pointing to a new instance of).
This works basically like a tape, so our StringIO instance has been rewound and is at the beginning of the string input we captured.
expect($stdout.gets).to eq(" O | X | O \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" X | O | X \n")
expect($stdout.gets).to eq("-----------\n")
expect($stdout.gets).to eq(" O | X | O \n")
$stdout.gets simply gets the first line of the output we've captured. Any additional $stdout.gets will just get the next line, and then the one after that.
Each string it received included '\n', designating a line break within a string, so the '\n' was included for the test to pass.
Reference