如何存根ruby方法并根据对象的状态有条件地引发异常

如何存根ruby方法并根据对象的状态有条件地引发异常,ruby,rspec,tdd,rspec2,Ruby,Rspec,Tdd,Rspec2,简而言之,我希望通过存根方法引发异常,但前提是具有存根方法的对象具有特定状态 Mail::Message.any_instance.stub(:deliver) do if to == "notarealemailaddress!@#!@#" raise Exception, "SMTP Error" else return true end end 这不起作用,因为存根块内的上下文是:RSpec::Core::ExampleGroup::Nested_1::Nes

简而言之,我希望通过存根方法引发异常,但前提是具有存根方法的对象具有特定状态

Mail::Message.any_instance.stub(:deliver) do
  if to == "notarealemailaddress!@#!@#"
    raise Exception, "SMTP Error"
  else
    return true
  end
end
这不起作用,因为存根块内的上下文是:RSpec::Core::ExampleGroup::Nested_1::Nested_2::Nested_2

如何访问存根对象?

使用Ruby2,rspec2


实际情况是,我有一个应用程序,可以批量发出数千封电子邮件,我有代码可以捕获SMTP异常、记录批处理并继续。因此,我想测试发送几个批次,其中中间的一个批次抛出异常。 我相信您是使用
with
方法指定参数的,因此在您的情况下,它应该是以下内容:

Mail::Message.any_instance.stub(:deliver).with(to: "notarealemailaddress!@#!@#") do
    raise Exception, "SMTP Error"
end
这里有完整的文档:

最新版本(目前为alpha)的Rspec v3似乎解决了这一问题:


好的,下面介绍了如何在不升级的情况下轻松获得此行为:

class Rspec::Mocks::MessageExpectation
  # pulling in behavior from rspec v3 that I really really really need, ok?
  # when upgrading to v3, delete me!
  def invoke_with_orig_object(parent_stub, *args, &block)
    raise "Delete me.  I was just stubbed to pull in behavior from RSpec v3 before it was production ready to fix a bug!  But now I see you are using Rspec v3. See this commit: https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643" if RSpec::Version::STRING > "2.99.0.pre"
    args.unshift(@method_double.object)
    invoke_without_orig_object(parent_stub, *args, &block)
  end
  alias_method_chain :invoke, :orig_object
end

将其放在等级库文件的底部。您会注意到,我甚至添加了一个检查,以在RSpec升级后引发一个错误。轰

我认为这行不通。当您指定#with时,我认为不会执行该块。最好不要在测试中使用条件。为什么不创建两个测试,一个是肯定的,一个是否定的?因为我在迭代数组时测试异常处理,本质上是这样的。因此,我希望数组中的一个对象在调用其上的方法时抛出异常;因此,我需要存根一个方法,但只能在一个特定的对象上存根,而我在测试中无法访问该对象,因为我正在测试的方法在内部创建对象。也许这是一种气味的暗示,但我正在为其他人的代码编写测试,我不想做太多更改。+1很好的发现和迷人的测试。我对RSpec使用自身验证自身的一些方式感到惊讶。
class Rspec::Mocks::MessageExpectation
  # pulling in behavior from rspec v3 that I really really really need, ok?
  # when upgrading to v3, delete me!
  def invoke_with_orig_object(parent_stub, *args, &block)
    raise "Delete me.  I was just stubbed to pull in behavior from RSpec v3 before it was production ready to fix a bug!  But now I see you are using Rspec v3. See this commit: https://github.com/rspec/rspec-mocks/commit/ebd1cdae3eed620bd9d9ab08282581ebc2248535#diff-060466b2a68739ac2a2798a9b2e78643" if RSpec::Version::STRING > "2.99.0.pre"
    args.unshift(@method_double.object)
    invoke_without_orig_object(parent_stub, *args, &block)
  end
  alias_method_chain :invoke, :orig_object
end