提示用户输入时rspec 3错误

时间:2014-12-13 00:33:37

标签: ruby unit-testing rspec3

我有要求用户输入的代码,例如:

class Foo
  def prompt_for_foobar
    puts "where is the foobar?"
    gets.chomp
  end
end

我想测试我的应用程序是在询问“foobar在哪里?”。我的测试将通过评论'gets.chomp'。但这是必要的,我尝试的其他任何事情都给了我一个Errno :: ENOENT:错误。

it "should prompt user" do
  console = Foo.new
  request = "where is the foobar?"
  expect { console.prompt_for_foobar }.to output(request).to_stdout
end

测试此方法的最佳方法是什么?

2 个答案:

答案 0 :(得分:0)

不确定这是否是处理此问题的最佳方式,但您可以将putsgets发送至STDOUTSTDIN

class Foo
  def prompt_for_foobar
    STDOUT.puts "where is the foobar?"
    STDIN.gets.chomp
  end
end

然后,测试STDIN收到带有您所需对象的puts条消息。

describe Foo do
  let(:foo) { Foo.new }

  before(:each) do
    allow(STDIN).to receive(:gets) { "user input" }
  end

  describe "#prompt_for_foobar" do
    it "prompts the user" do
      expect(STDOUT).to receive(:puts).with("where is the foobar?")

      foo.prompt_for_foobar
    end

    it "returns input from the user" do
      allow(STDOUT).to receive(:puts)

      expect(foo.prompt_for_foobar).to eq "user input"
    end
  end
end

答案 1 :(得分:0)

问题是gets是一种强制人类交互的方法(至少在RSpec的上下文中,stdin没有连接到另一个进程的管道),但是自动化测试工具的全部要点像RSpec一样,能够在不涉及人为干预的情况下运行测试。

因此,我建议您依赖于实现特定接口的协作者对象,而不是直接依赖于gets,在测试环境中,您可以提供该接口的实现,没有人工交互的响应,在其他环境中,它可以使用gets来提供响应。这里最简单的协作者界面可能是一个proc(它们非常适合这种事情!),所以你可以做到以下几点:

class Foo
  def prompt_for_foobar(&responder)
    responder ||= lambda { gets }
    puts "where is the foobar?"
    responder.call.chomp
  end
end

RSpec.describe Foo do
  it 'prompts the user to respond' do
    expect { Foo.new.prompt_for_foobar { "" } }.to output(/where is the foobar/).to_stdout
  end

  it "returns the responder's response" do
    expect(Foo.new.prompt_for_foobar { "response" }).to eq("response")
  end
end

请注意prompt_for_foobar不再直接调用gets;相反,它委派了对responder合作者的回复的责任。默认情况下,如果未提供响应者,则使用gets作为响应者的默认实现。在您的测试中,您可以通过传递返回字符串的块来轻松提供不需要人工交互的响应者。