ruby - 用 RSpec 包裹在 block 中的测试方法

标签 ruby mocking block rspec2

在我实际操作的简化示例中,假设我有 2 次对数据库的调用:

Repo.add( something_stringy )
Repo.remove( something_floaty )

我想对数据库调用使用 mock,因为真正的调用将在别处进行测试:

let(:repo){
  repo = double("Repo")
  repo.should_receive(:add).with(instance_of(String))
  repo.should_receive(:remove).with(instance_of(Float))
  repo
}

before { FakeKlass.const_set :Repo, repo }

这一切都很好,但现在如果我将调用包装在一个事务中,我会有点难过:

Repo.transaction do
  # ... some error checking in here somewhere...
  Repo.add( something_stringy )
  Repo.remove( something_floaty )
end

因为如果我编写一个接收 transaction 的 mock,它会收到调用,但 block 中的所有内容都不会被调用,我得到:

expected: 1 time received: 0 times

对于所有其他模拟。谁能告诉我应该如何编写规范来处理这个问题?我已经尝试阅读 RSpec 书中关于 around(:each) 的相关页面,但对我来说那是一团糟。

非常感谢任何帮助。

最佳答案

您可以使用#and_yield 从期望链中产生 yield :

repo.should_receive( :transaction ).and_yield

您也不需要在 Repo 类上设置一个 double 来 stub 方法。例如,您的设置可以这样写:

before( :each ) do
    Repo.should_receive( :transaction ).and_yield
    Repo.should_receive( :add ).with( instance_of(String) )
    Repo.should_receive( :remove ).with( instance_of(Float) )
end

您还可以考虑使用 stub 而不是 should_receive,因为它不会设置期望:

before( :each ) do
    Repo.stub( :transaction ).and_yield
    Repo.stub( :add )
    Repo.stub( :remove )
end

一般来说,当您想要测试两个对象之间的交互时,您应该只使用should_receive。我个人的经验法则是,如果它出现在before中,则使用stub;如果它在示例中,尤其是具有特定值,请使用 should_receive

关于ruby - 用 RSpec 包裹在 block 中的测试方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5958475/

相关文章:

ruby-on-rails - Carrierwave通过url获取图片

Swift 和 Salesforce SDK,使用 SFRestDictionaryResponseBlock

magento - 覆盖\app\code\core\Mage\Core\Block\Messages.php

ruby - Ruby RegExp 中的可选命名组

ruby-on-rails - 如何在 Rails 5 API only app 中设置 JSON API 的内容类型

java - 如何模拟一个方面

ios - OCMock "expected method was not invoked"即使我自己调用它

ruby - 如何限制 block 被调用的次数?

ruby - 如何在 ruby​​ 中建立摊销表?

java - 模拟静态方法的替代方法