【发布时间】:2014-06-14 12:25:09
【问题描述】:
我目前正在尝试测试一种基本方法,该方法接收来自用户的一些输入(gets)并输出它(puts)。经过一番研究,我找到了一种测试标准输出流的好方法,如下所示:
def capture_standard_output(&block)
original_stream = $stdout
$stdout = mock = StringIO.new
yield
mock.string.chomp
ensure
$stdout = original_stream
end
我正在测试的方法是下面的方法,输出和输入指的是我在开头初始化并指向等效的 $stdout 和 $stdin 的 ivars:
def ask_for_mark
ouput.puts 'What shall I call you today?'
answer = input.gets.chomp.capitalize
answer
end
现在我已经看到了一些针对 STDIN 的解决方案,但还没有真正理解其中的任何一个,我绝对不想复制和粘贴。我唯一要“工作”的是下面的那个,但它并没有真正起作用,因为当我运行 rspec 时它会暂停并等待输入,只需按 Enter,它就会通过:
it "takes user's name and returns it" do
output = capture_standard_output { game.ask_for_name }
expect(output).to eq "What shall I call you today?"
game.input.stub(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
什么是测试 STDIN 的好方法?我整天大部分时间都在盯着屏幕(我知道这不太好),因此非常感谢您提供新的视角和一些帮助:-)
更新
对于任何面临类似问题的人,我采用了不同(更简单)的方法。首先,我会在各自的方法中分离 IO 操作。
在输入的情况下,在测试中可以预先填充所需的数据,因此当发送gets 消息时,它将返回由换行符\n 分隔的数据,如下所示:
input = StringIO.new("one\ntwo\n")
=> #<StringIO:0x007f88152f3510>
input.gets
=> "one\n"
input.gets
=> "two\n"
input.gets
=> nil
这有助于将被测方法的内部保持为私有,而无需将测试与实现细节耦合。
另一种方法是简单地使用多态性并传入符合相同 api 的 Fake 或 Spy 对象,但不是调用 stdin 或 stdout,而是返回罐头数据,或者在间谍,它会注册呼叫。
class SpyIO
def initialize
was_called? = false
end
...
def ask_for_name
# call to stdout would normally take place
was_called? = true
end
...
end
class FakeIO
def initialize(data = [some, data])
@data = data
end
def get_user_input
# call to stdin would normally happen
@data.shift
end
...
end
每种方法都有权衡,但我想我会把这些放在这里,以防有人遇到类似问题或考虑选择。
【问题讨论】: