我怎样才能适当地模拟一个返回收益的方法?

时间:2016-07-12 12:33:44

标签: ruby unit-testing rspec mocha

在Ruby中,使用块看起来像这样的方法相当普遍:

class File
  def open(path, mode)
    perform_some_setup
    yield
  ensure
    do_some_teardown
  end
end

对于看起来像这样的方法来说,这也是相当惯用的:

def frobnicate
  File.open('/path/to/something', 'r') do |f|
    f.grep(/foo/).first
  end
end

我想为此编写一个不会触及文件系统的规范,这样可以确保它从文件中删除正确的单词,例如:

describe 'frobnicate' do
  it 'returns the first line containing the substring foo' do
    File.expects(:open).yields(StringIO.new(<<EOF))
      not this line
      foo bar baz
      not this line either
    EOF
    expect(frobnicate).to match(/foo bar baz/)  
  end
end

问题在于,通过模拟对File.open的调用,我还删除了其返回值,这意味着frobnicate将返回nil。但是,如果我要向链中添加File.returns('foo bar baz')这样的内容,我最终会得到一个并未实际触及我感兴趣的任何代码的测试。 <{1}}中的块内容可以做任何事情,测试仍然可以通过。

如果不点击文件系统,我如何适当地测试我的frobnicate方法?我并不特别依赖于任何特定的测试框架,所以如果你的答案是&#34;请使用这个为你做的真棒宝石&#34;然后我就可以了。

2 个答案:

答案 0 :(得分:1)

看起来你只需要稍微嘲笑对File的调用。我在运行你的代码时遇到了语法错误,所以我不确定你所使用的是什么版本的RSpec,但是如果你使用的是3.x就可以完成这项工作:

frobnicate_spec.rb

gem 'rspec', '~> 3.4.0'
require 'rspec/autorun'

RSpec.configure do |config|
  config.mock_with :rspec
end

def frobnicate
  File.open('/path/to/something', 'r') do |f|
    f.grep(/foo/).first
  end
end

RSpec.describe 'frobnicate' do
  it 'returns the first line containing the substring foo' do
    allow(File).to receive(:open).and_call_original
    allow(File).to receive(:open).and_yield StringIO.new <<-EOF
      not this line
      foo bar baz
      not this line either
    EOF
    expect(frobnicate).to match(/foo bar baz/)
  end
end

使用ruby frobnicate_spec.rb调用,以便我们可以使用指定的RSpec版本。

来源:RSpec模拟expecting messagesyielding responses

答案 1 :(得分:0)

使用minitest可以像我在下面发布的那样完成。我添加了整个runnable文件,因此您可以使用ruby -Ilib:test test_file.rb从命令行进行测试:

def frobnicate
  found_string = nil
  File.open('/path/to/something', 'r') do |f|
    found_string = f.grep(/foo/).first
  end
  found_string
end

class FrabnicateTest < Minitest::Test
  def test_it_works
    mock_file = StringIO.new(%(
      not this line
      foo bar baz
      not hthis line either
    ))
    search_result = nil
    File.stub(:open, nil, mock_file) do
      search_result = frobnicate
    end
    assert_match(/foo bar baz/, search_result)
  end
end