KOB
KOB

Reputation: 4545

How can I unit test a method that takes stdin within a loop?

I have the following method in class QuestionList

def ask_all
    @questions.each do |question|
        question.ask
        @player.add_answer(gets.chomp)
    end
end

The questions have integer answers, and the answers do not have to be correct for this test - there simply just needs to be an integer number received and added to the list @player.answers with the following method in class Player

def add_answer(answer)
    @answers << answer
end

How can I simulate user input to the gets.chomp of QuestionList.ask_all when unit testing the method as so:

class QuestionListTest < Test::Unit::TestCase

    def setup
        ...
    end

    def test_ask_all
        #unit test for 'QuestionList.ask_all' here
    end

end

Upvotes: 0

Views: 72

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

1. Modify ask_all to be input-agnostic:

def ask_all
    @questions.each do |question|
        question.ask
        @player.add_answer(question.retrieve_answer)
    end
end

2. Modify Question class to have retrieve_answer method:

class Question
  def ask
    # even better, delegate this to some other class
    #  to make it possible to work with console,
    #  batches, whatever depending on settings
    print "Enter an answer >"
  end
  def retrieve_answer
    # for now it’s enough
    gets.chomp
  end
end

3. Mock Question class to “ask questions” not interactively:

class QuestionListTest < Test::Unit::TestCase

    def setup
      Question.define_method :ask do
        print "I am mock for prompting ... "
      end
      Question.define_method :retrieve_answer do
        # be aware of wrong input as well!
        [*(1..10), 'wrong input', ''].sample.tap do |answer|
          puts "mocked answer is #{answer}." 
        end
      end
    end

    def test_ask_all
      expect(ask_all).to match_array(...)
    end
end

Upvotes: 1

Related Questions