我怎样才能适当地模拟出 returns 产生的方法?
How can I appropriately mock out a method that returns yield?
在 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
的调用,我还删除了它的 return 值,这意味着 frobnicate
将 return nil
。但是,如果我向链中添加 File.returns('foo bar baz')
之类的东西,我最终会得到一个实际上不会命中任何我感兴趣的代码的测试; frobnicate
中的块内容可以做任何事情,测试仍然会通过。
如何在不影响文件系统的情况下适当地测试我的 frobnicate
方法?我并不特别依赖任何特定的测试框架,所以如果你的答案是 "use this awesome gem that'll do it for you" 那么我同意。
看来您只需要稍微不同地模拟对 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 messages and yielding responses
使用 minitest 可以像下面我 post 那样完成。我已经添加了整个可运行文件,因此您可以使用 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
在 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
的调用,我还删除了它的 return 值,这意味着 frobnicate
将 return nil
。但是,如果我向链中添加 File.returns('foo bar baz')
之类的东西,我最终会得到一个实际上不会命中任何我感兴趣的代码的测试; frobnicate
中的块内容可以做任何事情,测试仍然会通过。
如何在不影响文件系统的情况下适当地测试我的 frobnicate
方法?我并不特别依赖任何特定的测试框架,所以如果你的答案是 "use this awesome gem that'll do it for you" 那么我同意。
看来您只需要稍微不同地模拟对 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 messages and yielding responses
使用 minitest 可以像下面我 post 那样完成。我已经添加了整个可运行文件,因此您可以使用 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