在Ruby中测试STDIN

我目前正在尝试测试一个基本方法,它接收来自用户的一些输入(获取)并输出它(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") => # input.gets => "one\n" input.gets => "two\n" input.gets => nil 

这有助于保持被测方法的内部,私有而不将测试与实现细节相结合。

另一种方法是简单地使用多态并传入符合相同api的Fake或Spy对象,但不是调用stdinstdout ,而是返回固定数据,或者在Spy的情况下,它会注册调用。

 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 

每种方法都有权衡,但我认为我会把这些放在这里以防万一有人遇到类似问题或考虑选项。

你可以简单地存根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

 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 

好吧,我真的不能确定,但​​是当我使用$ stdin / $ stdout / StringIO时,我有一个问题(仍然有)确保在它们上运行#rewind。 你在做吗?

 input = StringIO.new input.puts "joe" input.rewind input.gets #=> "joe\n"