你如何存根File.open块中发生的file.read?

问题描述 投票:5回答:3

如何存根file.read调用以便它返回我想要的内容?以下不起作用:

def write_something
  File.open('file.txt') do |f|
    return contents = f.read
  end
end
# rspec
describe 'stub .read' do
  it 'should work' do
    File.stub(:read) { 'stubbed read' }
    write_something.should == 'stubbed read'
  end
end

看起来这个存根被应用于File类而不是我块中的文件实例。所以File.read按预期返回stubbed read。但是当我运行我的规范时,它失败了。

ruby rspec
3个回答
11
投票

我应该注意到File.open只是Ruby非常大的I / O API的一部分,所以你的测试可能非常强烈地与你的实现相结合,并且不太可能在重构过程中存活下来。此外,必须小心“全局”模拟(即常量或所有实例),因为它可能无意中模仿其他地方的用法,导致混乱的错误和失败。

而不是模拟,考虑在磁盘上创建实际文件(使用Tempfile)或使用更广泛的I / O模拟库(例如FakeFS)。

如果您仍然希望使用模拟,您可以在某种程度上安全地将File.open存根以产生一个double(并且只有在使用正确的参数调用时):

file = instance_double(File, read: 'stubbed read')
allow(File).to receive(:open).and_call_original
allow(File).to receive(:open).with('file.txt') { |&block| block.call(file) }

或者,有点危险,存根所有实例:

allow_any_instance_of(File).to receive(:read).and_return('stubbed read')

1
投票

主要的一点是让File.open返回一个对象,它会响应你想要的内容read,这里是代码:

    it "how to mock ruby File.open with rspec 3.4" do
      filename = 'somefile.txt'
      content = "this would be the content of the file"
      # this is how to mock File.open:
      allow(File).to receive(:open).with(filename, 'r').and_yield( StringIO.new(content) )
      # you can have more then one of this allow

      # simple test to see that StringIO responds to read()
      expect(StringIO.new(content).read).to eq(content)

      result = ""
      File.open('somefile.txt', 'r') { |f| result = f.read }
      expect(result).to eq(content)
    end

0
投票

我就是这样做的

    describe 'write_something' do
      it 'should work' do  
        file_double = instance_double('File')
        expect(File).to receive(:open).with('file.txt').and_yield(file_double)
        expect(file_double).to receive(:read).and_return('file content')
        content = write_something
        expect(content).to eq('file content')
      end
    end  
© www.soinside.com 2019 - 2024. All rights reserved.