在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;然后我就可以了。
答案 0 :(得分:1)
看起来你只需要稍微嘲笑对File
的调用。我在运行你的代码时遇到了语法错误,所以我不确定你所使用的是什么版本的RSpec,但是如果你使用的是3.x就可以完成这项工作:
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 messages和yielding 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