在Ruby中测试STDIN

时间:2014-04-28 19:45:51

标签: ruby testing rspec stdin

我目前正在尝试测试一个从用户接收一些输入(获取)并输出它(put)的基本方法。经过一番研究后,我找到了测试标准输出流的好方法,如下所示:

def capture_standard_output(&block)
  original_stream = $stdout
  $stdout = mock = StringIO.new
  yield
  mock.string.chomp
ensure
  $stdout = original_stream
end

我测试的方法是下面的方法,输出和输入是指我在开始时初始化并指向等效的$ stdout&的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消息时,它将返回由换行符{{1}分隔的数据像这样:

\n

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

另一种方法是简单地使用多态并传入符合相同api的Fake或Spy对象,但不是调用input = StringIO.new("one\ntwo\n") => #<StringIO:0x007f88152f3510> input.gets => "one\n" input.gets => "two\n" input.gets => nil stdin,而是返回固定数据或者在Spy的情况下,它会注册呼叫。

stdout

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

2 个答案:

答案 0 :(得分:18)

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

答案 1 :(得分:3)

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

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