在测试结束前验证 rspec 个模拟

Verifying rspec mocks before the test is over

似乎在测试用例中使用 rspec 模拟的标准方法是执行如下操作:

class MyTest
  def setup
    super
    ::RSpec::Mocks.setup(self)
  end

  def teardown
    super
    begin
      ::RSpec::Mocks.verify
    ensure
      ::RSpec::Mocks.teardown
    end
  end

  test "something"
    foo = MyFoo.new
    expect(foo).to receive(:bar).and_return(42)
    ret = SomeClass.call_bar(foo)

    assert_equal(42, ret)
  end
end

有效好的。但是如果 SomeClass.call_bar 使用 foo.bar 的 return 作为 return,并且代码有问题以至于 foo.bar 从未被调用,那么我只会收到由于 assert_equal(42, ret) 行而失败。我没有看到任何错误,例如:

RSpec::Mocks::MockExpectationError: (foo).bar
    expected: 1 time
    received: 0 times

如果我删除 assert_equal(42, ret) 行,那么我会得到 rspec 期望错误。但我想验证这两件事,即 foo.bar 被称为 最后的 return 是 42。更重要的是要知道 foo.bar 不是t 调用是因为这就是 42 未被 returned 的原因。

如果我期待这样的事情:expect(foo).not_to receive(:bar),那么我确实在调用的源头得到了期望错误,而不是在拆解的后期。

现在,我可以在调用 assert_equal 之前执行类似放置 ::RSpec::Mocks.verify 的操作,但这感觉不对。我也不确定此时是否应该清理模拟。

有没有像这样的语法:

  test "something"
    foo = MyFoo.new
    ret = nil

    expect(foo).to receive(:bar).and_return(42).during do
      ret = SomeClass.call_bar(foo)
    end

    assert_equal(42, ret)
  end

以便在块传递给 during 后立即进行验证?或者如果你有多个双打,你可以这样做:

    expect(dbl1).to receive(:one)
    expect(dbl2).to receive(:two)
    expect(dbl3).to receive(:three)

    verify(dbl1, dbl2, dbl3).during do
      my_code
    end

我认为没有任何 built-in 方法可以做到这一点,但是如果您添加以下内容 class:

class VerifyDuring
  def initialize(test, objects)
    @test = test
    @objects = objects
  end

  def during
    yield
  ensure
    begin
      @objects.each do |object|
        RSpec::Mocks.proxy_for(object).verify
      end
    rescue Exception => e
      @test.flunk e
    end
  end
end

还有下面的方法给你测试class:

  def verify(*objects)
    VerifyDuring.new(self, objects)
  end

你可以这样做:

    verify(dbl1, dbl2, dbl3).during do
      my_code
    end

您正在寻找 rspec spies

Spies are an alternate type of test double that support this pattern by allowing you to expect that a message has been received after the fact, using have_received.

您使用 allow(...).to receivefoo 中创建了一个部分双倍,然后可以声明消息的接收:

test "something"
  foo = MyFoo.new
  allow(foo).to receive(:bar).and_return(42)
  ret = SomeClass.call_bar(foo)
  expect(foo).to have_received(:bar)
  assert_equal(42, ret)
end

我相信你需要的是聚合失败 https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/aggregating-failures

在 "normal" 设置中,任何错误都会中止测试并且不会检查后面的断言。