【问题标题】:How to test a function which takes a block with rspec如何测试一个带有 rspec 块的函数
【发布时间】:2013-02-04 00:25:55
【问题描述】:

我有一个函数,它接受一个块,打开一个文件,生成并返回:

def start &block
    .....do some stuff
    File.open("filename", "w") do |f|
        f.write("something")
        ....do some more stuff
        yield
    end
end

我正在尝试使用 rspec 为其编写测试。如何存根 File.open 以便将对象 f (由我提供)传递给块,而不是尝试打开实际文件?比如:

it "should test something" do
    myobject = double("File", {'write' => true})
    File.should_receive(:open).with(&blk) do |myobject|
        f.should_receive(:write)
        blk.should_receive(:yield) (or somethig like that)
    end
end

【问题讨论】:

  • 这很难测试,因为您要测试的是方法内部。您的文件如何与方法之外的任何内容交互?从您的示例中,您根本不会在方法之外使用该文件,也不会产生它。如果这个函数之外的东西需要文件,那么也许你应该考虑将文件对象传递给方法(依赖注入)。然后你可以模拟文件没问题。如果您确实需要测试有关该文件的某些内容,也许您应该考虑将其分解为一个单独的方法?
  • 谢谢...做了类似的事情,分成多个方法,存根几个函数,如 File.stub(open).and_yield(my_double) 等。

标签: ruby rspec mocking rspec2 stubbing


【解决方案1】:

您的另一个选择是使用 File 的新对象来存根 :open,例如:

file = File.new
allow(File).to receive(:open) { file }
file.each { |section| expect(section).to receive(:write) }
# run your method

【讨论】:

  • 小心这个。根据我的经验,我发现如果您在基本系统类(如File)上使用 RSpec 存根/模拟,它往往会导致很多问题。例如,如果您在测试中使用 VCR gem,如果您允许 (File).to 接收几乎所有内容,它将以惊人的方式中断。
【解决方案2】:

我想你要找的是yield matchers,即:

it "should test something" do
  # just an example
  expect { |b| my_object.start(&b) }.to yield_with_no_args
end

【讨论】:

  • 我如何存根 File.open 以在被测试的函数(开始)内提供一个模拟对象?
  • 让我重申一下 - 我如何存根 File.open 为其块提供模拟对象,以便我可以测试 f.should_receive(:write).with("应该写什么归档")
  • 我错过了您的评论,直到今天才点击“回复”。基本上 File.open 发送块 self (尝试从块内部运行puts f.inspect),所以模拟File.should_receive(:write) 应该可以工作。很抱歉回复晚了,但希望这对某人有用:)
  • 我通过模拟 File.should_receive(:open).and_yield(my_mock_object) 并在 my_mock_object.should_receive(:write) 上添加检查来做到这一点,等等……
  • @constantine1 您在评论中的解决方案正是我想要做的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-21
  • 2013-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多