谷歌模拟 - 我可以在同一个模拟对象上多次调用 EXPECT_CALL 吗?

如果我在同一个 TEST_F 中的同一个模拟对象上两次调用 EXPECT_CALL ...会发生什么?

If I call EXPECT_CALL twice on the same mock object in the same TEST_F . . . what happens?

是否将期望附加到模拟对象上,还是第二次调用消除了第一次调用的影响?

Are the expectations appended to the mock object or does the second call erase the effects of the first call?

我发现了之后子句似乎暗示允许多次调用同一个模拟+ EXPECT_CALL.

I found The After Clause which appears to imply that multiple calls to same mock + EXPECT_CALL are allowed.

推荐答案

是的,您可以在同一个模拟对象上多次调用 EXPECT_CALL.只要您确保在实际使用模拟方法之前调用了所有 EXPECT_CALL.否则你的测试将依赖于未定义的行为.来自 ForDummies:

Yes, you can call EXPECT_CALL on the same mock object multiple times. As long as you assure that all EXPECT_CALL were called before the mocked methods were actually used. Otherwise your test will rely on undefined behavior. From ForDummies:

重要说明:gMock 要求在调用模拟函数之前设置期望值,否则行为未定义.特别是,您不能将 EXPECT_CALL() 和对模拟函数的调用交织在一起.

Important note: gMock requires expectations to be set before the mock functions are called, otherwise the behavior is undefined. In particular, you mustn't interleave EXPECT_CALL()s and calls to the mock functions.

如何处理多个呼叫?文档非常简单.来自 ForDummies:

How multiple calls will be handled? The documentation is really straightforward. From ForDummies:

默认情况下,当调用一个模拟方法时,Google Mock 会搜索以相反的顺序定义期望,并在出现时停止找到与参数匹配的主动期望(你可以认为将其视为新规则覆盖旧规则.").

By default, when a mock method is invoked, Google Mock will search the expectations in the reverse order they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones.").

让我们通过一些示例来考虑这对 gMock 用户意味着什么.我假设我们有一个带有以下标题的文件:

Let's consider what this means for the gMock user, by checking some examples. I assume that we have a file with following header:

#include <gmock/gmock.h>

using namespace ::testing;

struct SomeMock
{
    MOCK_CONST_METHOD1(foo, void(int));
};

多次调用EXPECT_CALL的通过测试的最简单示例:

The simplest example of passing test that calls EXPECT_CALL multiple times:

TEST(Examples, DifferentArgumentsGoingToBeOk)
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
    EXPECT_CALL(mock, foo(5)).Times(1); // exp#2

    mock.foo(4); // call#1
    mock.foo(5); // call#2
}

测试直观:

  • call#1exp#2 不匹配,所以 exp#1 被尝试并匹配.
  • call#2exp#2 匹配.
  • call#1 does not match with exp#2 so exp#1 is tried and matches.
  • call#2 matches with exp#2.

两个调用只匹配一次,因此它们被认为是满意的并且测试通过.

Both calls matched exactly once, thus they are considered satisfied and the test passes.

当多个 EXPECT_CALL 能够匹配调用时,棘手的部分就开始了.让我们考虑以下示例:

The tricky part starts when multiple EXPECT_CALL are able to match the call. Let's consider the following example:

TEST(Examples, TheSameArgumentsGoingToFail) // Test fails!
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

  • call#1 匹配 exp#2.gMock 在第一个匹配的期望值处停止,它根本不会检查 exp#1.
  • call#2 匹配 exp#2.exp#1 再次没有机会被匹配.
    • The call#1 matches the exp#2. gMock stops at first matched expectation, it won't check the exp#1 at all.
    • The call#2 matches the exp#2. Again the exp#1 does not have chance to be matched.
    • 结果测试失败,因为 exp#2 被匹配两次而不是一次,并且 exp#1 根本不匹配.测试输出中打印的所有内容:

      As a result the test fails as the exp#2 gets matched twice instead of once and exp#1 is not matched at all. All that is printed in the test output:

      /tmp/so/main.cpp:26: Failure // exp#2
      Mock function called more times than expected - returning directly.
          Function call: foo(4)
               Expected: to be called once
                 Actual: called twice - over-saturated and active
      /tmp/so/main.cpp:25: Failure // exp#1
      Actual function call count doesn't match EXPECT_CALL(mock, foo(4))...
               Expected: to be called once
                 Actual: never called - unsatisfied and active
      

      此外,重要的是,添加新的预期不会禁用或删除旧的.他们仍然可以通过您的测试!

      Also, it is important, that adding new expectancy won't disable or remove old ones. They are still able to fail your test!

      TEST(Examples, NewExpectCallDoesNotEraseThePreviousOne) // Test fails!
      {
          SomeMock mock;
      
          EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
          EXPECT_CALL(mock, foo(4)).Times(2); // exp#2
      
          mock.foo(4); // call#1
          mock.foo(4); // call#2
      }
      

      call#1call#2 都匹配 exp#2.结果 exp#2 得到满足,但测试将失败,因为 exp#1 匹配的次数不够多.

      Both call#1 and call#2 matches the exp#2. As a result the exp#2 is satisfied, but the test will fail as exp#1 was not matched enough times.

      如果出于某种原因,您需要编写像 TheSameArgumentsGoingToFail 这样的测试,您可以使用多种技术来防止 exp#2 再次匹配.请参阅文档 InSequence 用法,RetiresOnSaturation:

      If for some reason, you need to write a test like TheSameArgumentsGoingToFail, you can use a number of techniques to prevent exp#2 from matching second time. Please refer to the documentation InSequence usage, RetiresOnSaturation:

      TEST(Examples, InSequenceExample)
      {
          SomeMock mock;
      
          Sequence seq;
      
          EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#1
          EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#2
      
          mock.foo(4); // call#1
          mock.foo(4); // call#2
      }
      
      TEST(Examples, InSequenceExampleSecondApproach)
      {
          SomeMock mock;
      
          InSequence seq;
      
          EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
          EXPECT_CALL(mock, foo(4)).Times(1); //exp#2
      
          mock.foo(4); // call#1
          mock.foo(4); // call#2
      }
      
      TEST(Examples, RetiresOnSaturationExample)
      {
          SomeMock mock;
      
          EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
          EXPECT_CALL(mock, foo(4)).Times(1).RetiresOnSaturation(); //exp#2
      
          mock.foo(4); // call#1
          mock.foo(4); // call#2
      }
      
      TEST(Examples, AfterExample)
      {
          SomeMock mock;
      
          auto& exp1 = EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
          EXPECT_CALL(mock, foo(4)).Times(1).After(exp1); //exp#2
      
          mock.foo(4); // call#1
          mock.foo(4); // call#2
      }
      

相关文章