如何为包含 'gets.chomp' 的变量的 Ruby 方法编写 Rspec 测试?

How do I write an Rspec test for a Ruby method that contains variable that contains 'gets.chomp'?

我查看了其他测试示例,但大多数其他示例不一定具有等于 'gets.chomp.downcase' 的变量,这让我很难进行测试。

剩下的是国际象棋游戏,但我正在努力做到这一点,所以如果您在介绍中输入 "new",它将调用方法#instructions,它会显示说明并询问是否你准备好玩了。

这是方法#introduction

def introduction
        puts " \n"
        print "     Welcome to chess! "
        puts "What would you like to do?"
        puts "

      * Start a new Game  ->  Enter 'new'
      * Load a saved Game ->  Enter 'load'

      * Exit              ->  Enter 'exit'"
      input = gets.chomp.downcase
      if input == "new"
        instructions
      elsif input == "load"
        load_game
      elsif input == "exit"
        exit!
      else 
        introduction
      end
    end

这是我对其进行的测试,它一直显示错误 "Failure/Error: input = gets.chomp.downcase"

"NoMethodError: nil:NilClass

的未定义方法 `chomp'
describe Game do
    describe "#introduction" do
        it "starts a new game with input 'new'" do

            io = StringIO.new
            io.puts "new"

            $stdin = io

            game = Game.new
            game.introduction
            allow(game).to receive(:gets).and_return(io.gets)

            expect(game).to receive(:instructions)
        end
    end
end

使用代码注入而不是模拟或存根

您的方法存在多个问题。我不会一一列举,而是重点关注三个关键错误:

  1. 单元测试通常应测试方法结果,而不是复制内部结构。
  2. 您正在尝试使用 #allow 而不先定义双精度数。
  3. 您似乎在尝试设置消息期望,而不是使用存根 return 一个值。

您的代码和测试肯定还有其他问题,但一旦您从测试用例中消除对#gets 的依赖,我就会从这里开始。例如,要测试方法中的各种路径,您应该为每个 expected value 配置一系列测试,其中 #and_return 显式 returns new, load,或其他什么。

更务实地说,您很可能会遇到困难,因为您首先编写了代码,现在正在尝试改进测试。虽然您可能会通过猴子修补程序使其可测试 post-facto,但您最好重构代码以允许在测试中直接注入。例如:

def show_prompt
  print prompt =<<~"EOF"

    Welcome to chess! What would you like to do?

      * Start a new Game  ->  Enter "new"
      * Load a saved Game ->  Enter "load"
      * Exit              ->  Enter "exit"

    Selection:\s
  EOF
end

def introduction input=nil
  show_prompt

  # Use an injected input value, if present.
  input ||= gets.chomp.downcase

  case input
  when "new"  then instructions
  when "load" then load_game
  when "exit" then exit!
  else introduction
  end
end

这避免了首先对对象进行存根或模拟的需要。您的测试现在可以简单地调用带有或不带显式值的#introduction。这使您可以将时间花在测试逻辑分支和方法输出上,而不是编写大量脚手架来支持模拟 IO#gets 调用或避免与 nil 相关的异常。