我如何为一个包含'get.chomp'变量的Ruby方法写一个Rspec测试?

问题描述 投票:0回答:1

我看了其他测试的例子,但大多数其他例子不一定有一个等于'get.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

这是我对它的测试,一直显示错误 "FailureError: input = gets.chomp.downcase"

"NoMethodError: undefined method `chomp' for nil:NilClass"

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
ruby unit-testing rspec io
1个回答
1
投票

使用代码注入来代替Mocks或存根

你的做法存在多个问题。我就不一一列举了,而是重点说说三个关键错误。

  1. 单元测试一般应该测试方法 结果,而不是复制内部。
  2. 你试图使用#allow而不先定义一个double。
  3. 你似乎试图设置一个消息期望,而不是使用一个存根来返回一个值。

当然,你的代码和测试还有其他问题,但是一旦你从测试用例中去除对#gets的依赖,我就会从这里开始。例如,为了测试你的方法中的各种路径,你可能应该为每条路径配置一系列的测试。预期值,其中#and_return明确地返回了 new, load,或者其他什么。

更实际的说,你很可能在挣扎,因为你先写了代码,现在又想改造测试。虽然你可能会对一些东西进行猴子补丁,使其在事后可以测试,但你可能最好重构你的代码,以允许在你的测试中直接注入。比如说,你可以使用

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相关的异常。

© www.soinside.com 2019 - 2024. All rights reserved.