2

在 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')的东西,我最终会得到一个实际上并没有触及我感兴趣的任何代码的测试;块中的内容frobnicate可以做任何事情并且测试仍然会通过。

我如何frobnicate在不影响文件系统的情况下适当地测试我的方法?我并不特别依赖任何特定的测试框架,所以如果你的答案是“使用这个可以为你做的很棒的 gem”,那么我可以接受。

4

2 回答 2

1

似乎您只需要以File不同的方式模拟调用即可。我在按原样运行您的代码时遇到语法错误,所以我不确定您使用的是哪个版本的 RSpec,但如果您使用的是 3.x,这将完成这项工作:

frobncate_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

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

资料来源:RSpec 模拟期望消息产生响应

于 2016-07-12T13:21:26.617 回答
0

使用 minitest 可以像我在下面发布的那样完成。我已经添加了整个可运行文件,因此您可以使用以下命令从命令行对其进行测试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
于 2016-07-12T13:47:13.567 回答