在 Ruby 中测试 STDIN [英] Testing STDIN in Ruby

查看:38
本文介绍了在 Ruby 中测试 STDIN的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试测试一种基本方法,该方法接收来自用户的一些输入(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对象,而不是调用stdinstdout,它返回固定数据,或者在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆