在 Ruby 中测试 STDIN [英] Testing STDIN in Ruby
问题描述
我目前正在尝试测试一种基本方法,该方法接收来自用户的一些输入(gets)并输出它(puts).经过一番研究,我找到了一种测试标准输出流的好方法,如下所示:
I'm currently trying to test a basic method that receives some input from the user (gets) and outputs it (puts). After a bit of research I found a good way to test the standard output stream which is the below:
def capture_standard_output(&block)
original_stream = $stdout
$stdout = mock = StringIO.new
yield
mock.string.chomp
ensure
$stdout = original_stream
end
我正在测试的方法是下面的方法,输出和输入指的是我在开始时初始化的 ivars 并指向等效的 $stdout &$标准输入:
The method I'm testing is the one below, output and input refer to ivars which I initialize in the beginning and point to the equivalent $stdout & $stdin:
def ask_for_mark
ouput.puts 'What shall I call you today?'
answer = input.gets.chomp.capitalize
answer
end
现在我已经看到了一些 STDIN 的解决方案,但还没有真正理解其中的任何一个,我绝对不想复制 &粘贴.我唯一一个工作"的人是下面的那个,但它并没有真正起作用,因为当我运行 rspec 时它会暂停并等待输入,只需按 Enter,它就会通过:
Now I've seen some solutions for STDIN but haven't really understood any of them and I definitely don't want to copy & paste. The only one I got to "work" is the one below, but it's not really working since when I run rspec it pauses and waits for input and by simply pressing enter, it passes:
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 的好方法?我一天中的大部分时间都在盯着屏幕(不好,我知道)所以非常感谢一个新的视角和一些帮助:-)
What would be a good way to test STDIN? I've been staring at a screen for most of the day (not good, I know) so a fresh perspective and some help would be greatly appreciated :-)
对于面临类似问题的任何人,我都采用了不同(更简单)的方法.首先,我会在他们自己的方法中分离IO操作.
For anyone facing similar issues, I followed different (simpler) approaches. First, I would separate the IO operations in their own methods.
在输入的情况下,在测试中它可以预先填充所需的数据,所以当 gets
消息被发送时,它将返回由换行符分隔的数据 <代码>\n 像这样:
In the case of input, in the tests it can be pre-populated with the desired data so when the gets
message is send, it will return that data which are separated by the newline character \n
like so:
input = StringIO.new("one\ntwo\n")
=> #<StringIO:0x007f88152f3510>
input.gets
=> "one\n"
input.gets
=> "two\n"
input.gets
=> nil
这有助于保持被测方法的内部结构的私有性,而无需将测试与实现细节耦合.
This helps with keep the internals of the method under test, private without coupling the tests to the implementation details.
另一种方法是简单地使用多态并传入符合相同api的Fake或Spy对象,而不是调用stdin
或stdout
,它返回固定数据,或者在 Spy 的情况下,它注册调用.
Another approach is to simply use polymorphism and pass in a Fake or a Spy object that conforms to the same api but instead of making a call to stdin
or stdout
, it returns canned data instead or in the case of the Spy, it registers the call.
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
每种方法都有权衡,但我想我会把它们放在这里,以防有人遇到类似问题或考虑选择.
There's tradeoffs in each approach but I thought I'd put these here in case anyone was having similar issues or considering options.
推荐答案
您可以简单地存根 STDIN:
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?"
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
实际上,你可以用 STDOUT
做同样的事情,而无需改变 $stdout
:
Actually, you can do the same with STDOUT
, without needing to change $stdout
:
it "takes user's name and returns it" do
expect(STDOUT).to receive(:puts).with("What shall I call you today?")
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
这篇关于在 Ruby 中测试 STDIN的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!