2

I am using RSpec to test my rock paper scissors game. Included in my begin_game function I have the following code:

user_input = gets.chomp.downcase.to_sym
while !choices.include? user_input
  puts "Please choose a valid selection : rock, paper, or scissors"
  user_input = gets.chomp.downcase.to_sym
end

I am trying to test for different possible user_inputs. I have tried this:

let(:new_game) {RockPaperScissors.new}
.......
context 'validate that the user input is one of the given choices' do
  it 'should prompt the user for a new input if the original one is invalid' do
  new_game.stub(:gets) {"r"}
  expect(new_game.begin_game).to eq("Please choose a valid selection : rock, paper, or scissors")
  end
end

but this results in an infinite loop of "Please choose a valid selection ..." being outputted to Terminal. I read the RSpec mocking documentation but it was difficult for me to understand.

1
  • 2
    I think you should be doing STDIN.stub(:gets) or Kernel.stub(:gets) instead of new_game.stub Commented May 21, 2014 at 0:53

1 Answer 1

3

The reason why it's looping is because new_game.stub(:gets) { "r" } will always return r no matter how many times you call it. Thus user_input will never contain valid input and your test will run forever.

To fix this, you should make new_game#gets return a valid selection after a certain number of tries.

For example,

new_game.stub(:gets) do
  @counter ||= 0
  response = if @counter > 3 # an arbitrary threshold
               "rock"
             else
               "r"
             end
  @counter += 1
  response
end

This would cause your test to print Please choose a valid selection... 4 times and then terminate.

Depending on how you implemented RockPaperScissors#begin_game, the test you wrote would still not pass. This is because puts("a string") will always return nil. Moreover, a while loop will also return nil. So at no point would the above snippet of code return the string "Please choose a valid selection : rock, paper, or scissors".

An implementation of begin_game that would pass is:

def begin_game
  user_input = gets.chomp.downcase.to_sym
  if choices.include? user_input
    # return something here
  else
    "Please choose a valid selection : rock, paper, or scissors"
  end
end

but at that point, I would probably rename it to handle_move, and have it accept an argument as a parameter to avoid stubbing gets in the first place.

def handle_move(input)
  if choices.include? input
    "Great move!"
  else
    "Please choose a valid selection : rock, paper, or scissors"
  end
end
Sign up to request clarification or add additional context in comments.

1 Comment

I just used a variation on this idea to test a related situation. It was really helpful. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.