DEV Community

Cover image for Testing Output With RSpec
Elle Hallal πŸ‘©πŸ½β€πŸ’»
Elle Hallal πŸ‘©πŸ½β€πŸ’»

Posted on β€’ Originally published at ellehallal.dev on

Testing Output With RSpec

Originally posted at ellehallal.devπŸ‘©πŸ½β€πŸ’»


This is a quick blog on testing output with RSpec. Currently, I am working on a Tic Tac Toe game in Ruby, where a user can play against a computer.


Previous test attempts

I’ve experienced issues testing a loop in the game, where it keeps requesting moves from players until the board is full or a player has won.

def main_game
  play_move until @game.over?
  end_of_game
end
Enter fullscreen mode Exit fullscreen mode

One attempt at testing this was to spy on the play_move and end_of_game methods. This worked fine if a full board was provided.

However, this was proving difficult to test when an incomplete board was present. Despite simulating user input, the test would hang at this stage. The same issue occurred when using object doubles.


Testing output

One of my mentors suggested testing the output of the end_of_game method. The end_of_game method prints the Tic Tac Toe board and the outcome of the game.

it 'plays a game that ends with a winning player' do

  expect { controller.main_game }
  .to output("""

  1 | x | x
----------------
  o | o | x
----------------
  x | o | o
The current player is x
Choose a position from 1-9:

  x | x | x
----------------
  o | o | x
----------------
  x | o | o
x is the winner!""")
  .to_stdout
Enter fullscreen mode Exit fullscreen mode

The multi-lines and alignment was also proving difficult to get right for the expected output. The key element that needed to be tested was the outcome of the game, which was the last line of the output: 'x is the winner!'


StringIO

StringIO is a β€˜string-based replacement for an IO object. It acts same as a file, but it’s kept in memory as a String’.

The idea is to catch the stream of output and redirect it. To check the last line of the output was correct, my mentor suggested trying the following:

it 'plays a game that ends with a winning player' do
  controller = controller_setup([1, 'x', 'x', 4, 'o', 'x', 'x', 8,     'o'])
  allow($stdin).to receive(:gets).and_return('8', '4', '1')
  $stdout = *StringIO*.new

  controller.main_game
  output = $stdout.string.split("\n")

  expect(output.last).to eq('x is the winner!')
end
Enter fullscreen mode Exit fullscreen mode

In this test:

  • A new instance of the controller class is created, with an incomplete board

  • The input is simulated to play the three remaining positions on the board

  • The standard output $stdin is redirected to a new instance of StringIO

  • The main_game method is called to play and complete the game

  • The output is then saved to the variable output, converted to a string, and split at the points where there is a new line to create an array of strings

  • The last item in the array is expected to be 'x is the winner!'

I’ll continue to use this method when testing large blocks of text with multiple lines. It’s significantly simpler than trying to align multi-line outputs in a test.


Resources

Heroku

Amplify your impact where it matters most β€” building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Jetbrains image

Build Secure, Ship Fast

Discover best practices to secure CI/CD without slowing down your pipeline.

Read more

πŸ‘‹ Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay