kittyminky
kittyminky

Reputation: 485

Rspec user input while loop tests

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.

Upvotes: 2

Views: 1554

Answers (1)

hjing
hjing

Reputation: 4982

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

Upvotes: 3

Related Questions