我看了其他测试的例子,但大多数其他例子不一定有一个等于'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
你的做法存在多个问题。我就不一一列举了,而是重点说说三个关键错误。
当然,你的代码和测试还有其他问题,但是一旦你从测试用例中去除对#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相关的异常。