Pete Fowler
blog.petefowler.dev

blog.petefowler.dev

Capturing multi-line console output in Ruby's RSpec testing interface

Photo by Constantin on Unsplash

Capturing multi-line console output in Ruby's RSpec testing interface

Pete Fowler's photo
Pete Fowler
·Oct 2, 2022·

3 min read

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

Ruby-Doc.org Definitions

  • $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

 
Share this