简体   繁体   中英

How do I test a function with gets.chomp in it?

I have a simple function using gets.chomp like this:

def welcome_user
   puts "Welcome! What would you like to do?"
   action = gets.chomp
end 

I'd like to test it using ruby 's built in TestCase suite like this:

class ViewTest < Test::Unit::TestCase
   def test_welcome
      welcome_user      
   end 
end 

The problem is, when I run that test, the gets.chomp stops the test because it needs the user to enter in something. Is there a way I can simulate user inputs using just ruby ?

You could create a pipe and assign its "read end" to $stdin . Writing to the pipe's "write end" then simulates user input.

Here's an example with a little helper method with_stdin for setting up the pipe:

require 'test/unit'

class View
  def read_user_input
    gets.chomp
  end
end

class ViewTest < Test::Unit::TestCase
  def test_read_user_input
    with_stdin do |user|
      user.puts "user input"
      assert_equal(View.new.read_user_input, "user input")
    end
  end

  def with_stdin
    stdin = $stdin             # remember $stdin
    $stdin, write = IO.pipe    # create pipe assigning its "read end" to $stdin
    yield write                # pass pipe's "write end" to block
  ensure
    write.close                # close pipe
    $stdin = stdin             # restore $stdin
  end
end

You first separate the 2 concerns of the method:

def get_action
  gets.chomp
end

def welcome_user
  puts "Welcome to Jamaica and have a nice day!"
  action = get_action
  return "Required action was #{action}."
end

And then you test the second one separately.

require 'minitest/spec'
require 'minitest/autorun'

describe "Welcoming users" do
  before do
    def get_action; "test string" end
  end

  it "should work" do
    welcome_user.must_equal "Required action was test string."
  end
end

As for the first one, you can

  1. Test it by hand and rely that it won't break (recommended approach, TDD is not a religion).
  2. Get the subverted version of the shell in question and make it imitate the user, and compare whether get_action indeed gets what the user types.

While this is a practical answer to your problem, I do not know how to do 2., I only know how to imitate the user behind the browser ( watir-webdriver ) and not behind the shell session.

You could inject the IO dependency. gets reads from STDIN , which is class IO . If you inject another IO object into your class, you can use StringIO in your tests. Something like this:

class Whatever
  attr_reader :action

  def initialize(input_stream, output_stream)
    @input_stream = input_stream
    @output_stream = output_stream
  end

  def welcome_user
    @output_stream.puts "Welcome! What would you like to do?"
    @action = get_input
  end

private

  def get_input
    @input_stream.gets.chomp
  end

end

Tests:

require 'test/unit'
require 'stringio'
require 'whatever'

class WhateverTest < Test::Unit::TestCase
  def test_welcome_user
    input = StringIO.new("something\n")
    output = StringIO.new
    whatever = Whatever.new(input, output)

    whatever.welcome_user

    assert_equal "Welcome! What would you like to do?\n", output.string
    assert_equal "something", whatever.action
  end
end

This allows your class to interact with any IO stream (TTY, file, network, etc.).

To use it on the console in production code, pass in STDIN and STDOUT :

require 'whatever'

whatever = Whatever.new STDIN, STDOUT
whatever.welcome_user

puts "Your action was #{whatever.action}"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM