【问题标题】:Rspec false positive because failure exception is rescued in code being testedRspec 误报,因为在正在测试的代码中挽救了失败异常
【发布时间】:2011-11-18 01:25:37
【问题描述】:

我有一个预期会失败的 rspec 测试,但它正在通过,因为它正在测试的代码挽救了 rspec 引发的异常。下面是一个例子:

class Thing do

  def self.method_being_tested( object )
    # ... do some stuff

    begin
      object.save!
    rescue Exception => e
      # Swallow the exception and log it
    end
  end

end

在 rspec 文件中:

describe "method_being_tested" do
  it "should not call 'save!' on the object passed in" do
    # ... set up the test conditions

    mock_object.should_not_receive( :save! )
    Thing.method_being_tested( mock_object )
  end
end

我知道执行已到达“object.save!”正在测试的方法的行,因此测试应该失败,但测试通过了。在救援块中使用调试器,我发现以下内容:

(rdb:1) p e # print the exception object "e"
#<RSpec::Mocks::MockExpectationError: (Mock "TestObject_1001").save!
    expected: 0 times
    received: 1 time>

所以基本上测试失败了,但是失败被它试图测试的代码所抑制。我想不出一种可行的方法来阻止此代码吞下 Rspec 异常,而不会以某种方式损害代码。我不希望代码明确检查异常是否是 Rspec 异常,因为那是糟糕的设计(应该为代码编写测试,永远不应该为测试编写代码)。但我也无法检查异常是否是我希望它捕获的任何特定类型,因为我希望它能够捕获在正常生产环境中可能引发的任何内容。

一定有人在我之前遇到过这个问题!请帮我找到解决方案。

【问题讨论】:

  • 你问是否有办法捕捉所有异常,除了你不想捕捉的那种,而不检查它在捕捉代码中是什么类型的异常。这里的答案是否定的。

标签: ruby-on-rails-3 exception rspec rescue false-positive


【解决方案1】:

假设代码是正确的:

describe "method_being_tested" do
  it "should not call 'save!' on the object passed in" do
    # ... set up the test conditions
    calls = 0
    mock_object.stub(:save!) { calls += 1 }
    expect {Thing.method_being_tested(mock_object)}.to_not change{calls}
  end
end

如果不需要绝对捕获所有异常,包括SystemExitNoMemoryErrorSignalException 等(来自@vito-botta 的输入):

begin
  object.save!
rescue StandardError => e
  # Swallow "normal" exceptions and log it
end

StandardErrorrescue 捕获的默认异常级别。

【讨论】:

    【解决方案2】:

    来自 rspec-mock:

    module RSpec
      module Mocks
        class MockExpectationError < Exception
        end
    
        class AmbiguousReturnError < StandardError
        end
      end
    end
    

    你真的需要抓住Exception吗?你能抓住StandardError吗?

    捕获所有异常通常是一件坏事。

    【讨论】:

    • @zetetic 可以说,捕获和忽略异常是一件坏事。捕获任何(或所有)异常并对其采取行动可能是完全合法的。
    【解决方案3】:

    我会这样重构它:

    class Thing do
    
      def self.method_being_tested!( object )
    
        # ... do some stuff
    
        return object.save
      end
    
    end
    

    如果你想忽略保存抛出的异常!调用保存没有意义!首先。您只需调用 save 并相应地通知调用代码。

    【讨论】:

    • 我不确定这是否是通用解决方案,如果保存!取而代之的是 API 调用,仍然会有处理异常的问题
    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多