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

google mock - can I call EXPECT_CALL multiple times on same mock object?

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

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

我发现 The After Clause 似乎暗示允许多次调用同一个模拟 + EXPECT_CALL。

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

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:

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 用户意味着什么。我假设我们有一个包含以下内容的文件 header:

#include <gmock/gmock.h>

using namespace ::testing;

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

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

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#2 匹配 exp#2.

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

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

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 没有机会被匹配。

因此测试失败,因为 exp#2 匹配了两次而不是一次,而 exp#1 根本不匹配。测试输出中打印的所有内容:

/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

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

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 没有匹配足够的次数。

如果由于某种原因,你需要写一个像TheSameArgumentsGoingToFail这样的测试,你可以使用一些技巧来防止exp#2进行第二次匹配。请参考文档 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
}

另一个有用的技巧(在傻瓜指南中也有介绍)是只写一个 EXPECT_CALL 但将多组操作链接起来以指示预期结果。例如:

SomeMock mock;

EXPECT_CALL(mock, foo(4))
    .WillOnce(Return(16))
    .WillOnce(Return(42))
    .WillOnce(Throw(MyException()));

这需要对具有相同参数的方法进行三次调用,前两次将 return 指定的值,然后在第三次调用时抛出异常。

这通常比使用多个 EXPECT_CALLRetiresOnSaturation 或其他技术更容易理解。

您也可以将它与 void 方法一起使用;您只需要使用 DoDefault 或一些更有趣的操作来代替 Return.

A link 这个答案 as an article in my GitHub repo can be found in Google's gtest documentation here: googletest/docs/community_created_documentation.md.


以下所有代码均使用 Googletest/Googlemock v1.10.0 进行了测试,该代码于 2019 年 10 月 3 日发布。

如果您想 运行 自己测试,但没有在系统上设置 googletest 或 googlemock,here's a bare-bones project I created它在 Ubuntu 上快速 运行ning。去克隆它并自己玩吧。它可以作为帮助您在 运行 或 Mac 或 Windows 上获得它的起点。

这是一个非常重要的问题,所以我觉得有必要对它进行破解。

细微差别:

让我先说 Google Mock (gmock) 是 细微差别 。这意味着有很多微妙之处需要理解,这很困难。甚至文档也有点零散,您需要仔细阅读和研究 全部 才能真正掌握其中的一些甚至大部分细微差别,因为它们做得不好在每份文件中重复某些要点。所以,这里是所有官方文档:如果你这样做是为了工作,请告诉你的主管你将留出几天时间仔细阅读 gtest 和 gmock 文档并练习示例以更好地掌握它.

文档:

当您阅读和研究以下文档时,将每个文档保存为(打印成)PDF,然后使用 Foxit Reader for free on Windows, Mac, or Linux, to edit, take notes in, and highlight or underline the PDF as you go. This way you have notes of the most important things you need to remember. See my *_GS_edit.pdf PDFs here and here 做笔记和标记我拥有的 PDF 的示例按照我所学 Google 测试和 Google 模拟完成。

官方Google文档:

  1. gtest:都在这个文件夹中:https://github.com/google/googletest/tree/master/googletest/docs。要研究的关键文件,按此顺序可能是:
    1. 底漆
    2. 常见问题解答
    3. samples(查看并仔细研究至少前3个样本的源代码)
    4. 高级
  2. gmock:都在这个文件夹中:https://github.com/google/googletest/tree/master/googlemock/docs。要研究的关键文件,按此顺序可能是:
    1. 假人
    2. 烹饪书
    3. 作弊 sheet - 这是所有文档中最好的 one-stop-shop 或“gmock 规则摘要”,但缺少一些内容甚至在(并且仅在)“for dummies”手册中明确说明,除了本文档之外,您还需要它。
    4. 常见问题解答
    5. 傻瓜 <-- 是的,再次!在完成并尝试编写大量测试和模拟之后,然后 回来 并再次 re-read 此文档!在您首先将 gtest 和 gmock 原则付诸实践之后,第二次这样做会更有意义。

一般要记住一些微妙的规则:

  1. “请记住,测试顺序是未定义的,因此您的代码不能依赖于另一个测试之前或之后的测试”(https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#sharing-resources-between-tests-in-the-same-test-suite)。
  2. "重要说明: gMock 要求在 调用模拟函数之前 设置预期,否则行为是 undefined。特别是,您不得交错 EXPECT_CALL()s 和对模拟函数的调用”(https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

答案:

问题 1:“如果我在同一个 TEST_F 中对同一个模拟对象调用 EXPECT_CALL 两次......会发生什么?”

答:首先,在这种情况下,无论您使用 TEST() 宏还是 TEST_F() 宏都没有区别。 TEST() 宏简单地扩展为 class,它公开继承自 ::testing::Test class,而 TEST_F() 宏简单地扩展为 class,它继承自您的测试夹具 class(TEST_F() 的第一个参数),它必须公开继承自 ::testing::Test class.

同一个mock对象(mockclass)可以调用很多EXPECT_CALL,从一般到具体,如下:

同一个mock对象上多个EXPECT_CALL的3条规则:
从最通用 --> 最具体(又名:“外部”-->“内部”范围)。

  1. 你可以至少每个模拟方法一个EXPECT_CALL一个模拟class 可以有许多模拟方法,因此每个方法可能有一个或多个 EXPECT_CALL 配置与该方法的预期交互。因此,模拟 class 每个方法可以有 至少 一个 EXPECT_CALL

  2. 在单个模拟方法上每个匹配器签名不应该超过一个 EXPECT_CALL:(阅读下面规则 3 中的更多内容)。每个模拟方法都有许多不同的参数 values 可以传入,因此每个匹配器签名可以 最多 一个 EXPECT_CALL (可能的参数值或值的组合,在多个输入参数的情况下)。这意味着每个模拟方法都可以可能有许多数千甚至数百万或数十亿有效和唯一EXPECT_CALL附加对它来说,每个都匹配一组不同的“匹配器”,或者将参数输入到模拟方法中。例如,这是完全有效的:

     // Each `EXPECT_CALL()` in this example has a different and 
     // unique "matcher" signature, so every `EXPECT_CALL()` will
     // take effect for its matching parameter signature when
     // `myMockMethod()` is called.
     //                                    v--matchers
     EXPECT_CALL(myMockClass, myMockMethod(1));
     EXPECT_CALL(myMockClass, myMockMethod(2));
     EXPECT_CALL(myMockClass, myMockMethod(3));
     EXPECT_CALL(myMockClass, myMockMethod(4));
     EXPECT_CALL(myMockClass, myMockMethod(5));
     ...
     EXPECT_CALL(myMockClass, myMockMethod(1000));
    

    特别是,上述 EXPECT_CALL 中的每一个都指定对具有匹配签名的 myMockMethod() 的调用必须恰好发生 1 次。这是因为在这种情况下,cardinality rules 指示隐含的 .Times(1) 出现在每个 EXPECT_CALL 上,即使您没有看到它被写入。

    要指定您希望给定 EXPECT_CALL 为给定参数匹配 任何 输入值,请使用 ::testing::_ 匹配器,如下所示:

     using ::testing::_;
    
     EXPECT_CALL(myMockClass, myMockMethod(_));
    
  3. 在同一个 mock 方法上没有 具有相同匹配器签名 的重复 EXPECT_CALL,但是 multiple EXPECT_CALLs 与 overlapping/overriding(但不重复)相同模拟方法上的匹配器签名是可以的: 如果您将多个 EXPECT_CALL 附加到相同的 匹配值 ,只有 最后一组 会起作用。例如,参见 here, here, and here。这意味着如果你有两个或更多 EXPECT_CALLs 具有重复的匹配器签名(传递给 mock 方法的相同参数),那么只有最后一个会得到任何调用。

    因此,您的测试将始终失败,除非在异常情况下,即除最后一个之外的所有 EXPECT_CALL 都具有 .Times(0) 值,指定它们将 永远不会 被调用,因为确实如此:最后一个 EXPECT_CALL 将匹配所有对这些匹配器的调用,并且它上面的所有重复 EXPECT_CALL 将具有 no[= 350=】匹配调用!这是一个测试示例,由于此行为,它总是失败。这是@luantkow 关注的主要行为 .

     using ::testing::_;
    
     // Notice they all have the same mock method parameter "matchers"
     // here, making only the last `EXPECT_CALL()` with this matcher
     // signature actually match and get called. Therefore, THIS TEST
     // WILL ***ALWAYS FAIL***, since EXPECT_CALL #1 expects to get 
     // called 1 time but is NEVER called, #2 through #1006, inclusive,
     // all expect to get called 2 times each but all of them are NEVER
     // called, etc.! Only #1007 is ever called, since it is last and
     // therefore always matches first.          
     //                                    v--matchers
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(1); // EXPECT_CALL #1
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #2
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #3
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #4
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #5
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #6
     // ... duplicate the line just above 1000 more times here
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(3); // EXPECT_CALL #1007
    

    然而,这个奇怪的异常使测试有效,只需将所有重复的 EXPECT_CALLs, 除了最后一个 设置为 .Times(0) 主要设置:

     using ::testing::_;
    
     // Notice they all have the same mock method parameter "matchers"
     // here, making only the last `EXPECT_CALL()` with this matcher
     // signature actually match and get called. However, since all previous
     // `EXCEPT_CALL` duplicates are set to `.Times(0)`, this test is valid
     // and can pass.          
     //                                    v--matchers
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #1
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #2
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #3
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #4
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #5
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #6
     // ... duplicate the line just above 1000 more times here
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(3); // EXPECT_CALL #1007
    

    在这里,只有 EXPECT_CALL #1007(最后一个 EXPECT_CALL)会匹配对 myMockMethod() 的调用,并且 Times(3) 会生效。由于此上面的所有重复 EXPECT_CALLs 将永远不会匹配和被调用,因为它们永远不会被达到,因此对于给定匹配器的重复 EXPECT_CALLs 的测试对于任何其他 .Times() 值总是失败比 .Times(0) 所有 non-last-place 重复 EXPECT_CALLs.

    这种让后面的匹配器能够覆盖前面的匹配器的效果是 故意的 并且是 Google 模拟设计的一部分,因为它允许您创建一个非常有用的预期调用层次结构,基于传递给模拟方法的值,如下所示:

     using ::testing::_;
    
     // Most general matchers first (_ matches any input value)
     EXPECT_CALL(myMockClass, myMockMethod(_)).Times(1);
     // More specific matchers next, to override the more general matcher 
     // above if they match
     EXPECT_CALL(myMockClass, myMockMethod(7)).Times(2);
     EXPECT_CALL(myMockClass, myMockMethod(5)).Times(4);
    

    各种 google 文档说匹配 EXPECT_CALL 的搜索顺序是 相反的 ,从 底部到顶部 。因此,如果调用 myMockMethod(8),它将根据此方法的最后一个 EXPECT_CALL 进行检查,该方法正在查找 myMockMethod(5)。那不匹配,所以它上升 1 并检查 myMockMethod(7)。那不匹配,所以它上升 1 并检查 myMockMethod(_)。这匹配!因此,它算作 Times(1) 基数授权的一次调用。

    所以,你上面定义的是这样的:我们期望 myMockMethod(5) 被调用 4 次,myMockMethod(7) 被调用 2 次,myMockMethod(anything_other_than_5_or_7) 被调用 1 次时间。有关此主题的更多阅读,请在此处查看我的其他答案:.

关键摘要: 关于“我可以在同一个模拟对象上多次调用 EXPECT_CALL 吗?”这个问题要记住的要点是:你只能如果匹配器(指定要传递给模拟方法的参数)不同[=350=,则对同一个模拟对象和方法多次调用EXPECT_CALL ] 每个 EXPECT_CALL。也就是说,当然,除非你在除最后一个重复项 EXPECT_CALL 之外的所有项上都设置了 .Times(0),这会使它们变得毫无用处,所以请记住不要让重复的 EXPECT_CALL 具有相同的匹配器。

这完全回答了这个问题。


问题 2:“是否将期望附加到模拟对象或第二次调用是否消除了第一次调用的影响?”

以上描述也回答了这个问题。本质上,EXPECT_CALL 期望不会覆盖它们之前的任何 EXPECT_CALL 的效果, 除非 匹配器(指定要传递给模拟方法的值)是相同或重叠,在这种情况下,只有 last EXPECT_CALL 会被调用,因为在匹配序列中它总是先于其他到达。因此,不要在给定的模拟方法上使用相同的匹配器重复 EXPECT_CALL,否则您可能会无意中强制测试 总是失败 ,因为上面的 EXPECT_CALLs 永远不会被调用。这在上面的问题 1 中进行了详细讨论。

再次强调,有关此主题的更多信息,请阅读上面的内容,然后查看我的其他答案这里:.


问题 3:我可以调用 EXPECT_CALL 对 mock 方法设置一些期望,调用 mock 方法,然后再次调用该方法 EXPECT_CALL 来改变期望,然后调用 mock又是方法?

OP 甚至没有明确 询问这个问题,但我找到这个页面的唯一原因是因为我已经搜索了这个答案很多个小时,但找不到找到它。我的 Google 搜索是“gmock multiple expect_call”。因此,其他人问这个问题也会落在这个页面,需要一个确定的答案。

A:不,你不能这样做!根据 Google,尽管它可能 似乎在测试中起作用 ,但它会产生 未定义的行为 。请参阅上面的一般规则 #2!

"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" (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

另请参阅我的回答:Interleaving EXPECT_CALL()s and calls to the mock functions is undefined behavior

因此,这是不允许的!:

// EXAMPLE OF A BAD TEST THAT MAY SEEM TO WORK BUT IS RELYING ON *UNDEFINED* BEHAVIOR!
// The goal is to ensure that `myMockMethod()` is only called 2x the first time by 
// `myOtherFunc()`, 3x the second time, and 0x the last time.

// Google states: "**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"
// (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

using ::testing::_;

TEST_F(MyTestFixture, MyCustomTest) 
{
    // `myMockMethod()` should be called only 2x here by `myOtherFunc()`,
    // despite calling `myOtherFunc()` repeatedly
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(2);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }

    // UNDEFINED BEHAVIOR BEGINS HERE: you can't interleave calls to `EXPECT_CALL` with 
    // calls to the mocked functions (in this case: `myMockMethod()`,
    // which is called by `myOtherFunc()`).

    // THEN `myMockMethod()` should be called 3x here by `myOtherFunc()`
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(3);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }

    // LAST, `myMockMethod()` should be called 0x here by `myOtherFunc()`
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(0);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }
}

那么,这里的有效解决方案是什么?好吧,如果你能把这个测试分成 3 个不同的、独立的测试,那就去做吧!但是,如果这 3 个测试以无法分开的方式相互关联怎么办?示例:您正在尝试测试一个节流功能,该功能将打印输出限制为每秒一次,例如,即使您尝试更频繁地打印每秒一次。那么,在这种情况下,有一些 work-arounds.

首先,让我们回顾一下:根据 Google Mock Cheat Sheet,这里是配置 EXPECT_CALL() 的方法:

EXPECT_CALL(mock-object, method (matchers)?)
     .With(multi-argument-matcher)  ?
     .Times(cardinality)            ?
     .InSequence(sequences)         *
     .After(expectations)           *
     .WillOnce(action)              *
     .WillRepeatedly(action)        ?
     .RetiresOnSaturation();        ?

For each item above, ? means it can be used at most once, while * means it can be used any number of times.

我们需要将 .WillRepeatedly(action) 选项与 action 一起使用,其中 produces side effects or calls a function, functor, or lambda 作为一个动作。

这里有一些 work-arounds 可以安全正确地执行上述具有未定义行为的测试。 如果您想首先查看最佳方法,请直接跳至下面的#3:

  1. 使用Assign(&variable, value). 在这种特殊情况下,这有点hacky,但它确实可以正常工作。对于您可能拥有的更简单的测试用例,这可能是完成所需操作的完美方式。这是一个可行的解决方案:

    旁注:我在尝试 运行 gmock 测试时得到的错误输出说:

    .Times() cannot appear after .InSequence(), .WillOnce(), .WillRepeatedly(), or .RetiresOnSaturation(),

    ...所以事实证明我们不需要(我们甚至 不允许 )在这里指定 .Times(::testing::AnyNumber())。相反,gmock 会根据 these cardinality rules 自动计算出来,因为我们使用的是 .WillRepeatedly():

    If you omit Times(), gMock will infer the cardinality for you. The rules are easy to remember:

    • If neither WillOnce() nor WillRepeatedly() is in the EXPECT_CALL(), the inferred cardinality is Times(1).
    • If there are n WillOnce()'s but no WillRepeatedly(), where n >= 1, the cardinality is Times(n).
    • If there are n WillOnce()'s and one WillRepeatedly(), where n >= 0, the cardinality is Times(AtLeast(n)).

    这项技术实际上已经过测试并证明适用于真实代码:

     using ::testing::_;
     using ::testing::Assign;
    
     TEST_F(MyTestFixture, MyCustomTest) 
     {
         bool myMockMethodWasCalled = false;
    
         EXPECT_CALL(MyMockClass, myMockMethod(_, _))
             // Set `myMockMethodWasCalled` to true every time `myMockMethod()` is called with
             // *any* input parameters!
             .WillRepeatedly(Assign(&myMockMethodWasCalled, true));
    
         // Do any necessary setup here for the 1st sub-test 
    
         // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
    
             if (i < 2)
             {
                 EXPECT_TRUE(myMockMethodWasCalled);
                 myMockMethodWasCalled = false;        // reset
                 EXPECT_FALSE(myMockMethodWasCalled);  // ensure reset works (sanity check)
             }
             else
             {
                 EXPECT_FALSE(myMockMethodWasCalled);
             }
         }
    
         // Do any necessary setup here for the 2nd sub-test
    
         // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
    
             if (i < 3)
             {
                 EXPECT_TRUE(myMockMethodWasCalled);
                 myMockMethodWasCalled = false;        // reset
                 EXPECT_FALSE(myMockMethodWasCalled);  // ensure reset works (sanity check)
             }
             else
             {
                 EXPECT_FALSE(myMockMethodWasCalled);
             }
         }
    
         // Do any necessary setup here for the 3rd sub-test
    
         // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
             EXPECT_FALSE(myMockMethodWasCalled);
         }
     }
    
  2. InvokeWithoutArgs(f)与全局计数器变量和全局计数器函数一起使用。这很好用,而且更容易使用,用途更广比以前的方法!请注意,如果需要,您也可以将此全局函数和变量迁移到您的测试夹具 class 中,这样可以稍微清理一下。

    这项技术实际上已经过测试并证明适用于真实代码:

     using ::testing::_;
     using ::testing::InvokeWithoutArgs;
    
     static uint32_t callCounter = 0;
     static void incrementCallCounter()
     {
         callCounter++;
     }
    
     TEST_F(MyTestFixture, MyCustomTest)
     {
         EXPECT_CALL(MyMockClass, myMockMethod(_, _))
             // Set gmock to increment the global `callCounter` variable every time 
             // `myMockMethod()` is called with *any* input parameters!
             .WillRepeatedly(InvokeWithoutArgs(incrementCallCounter));
    
         // Do any necessary setup here for the 1st sub-test 
    
         // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 2);
    
         // Do any necessary setup here for the 2nd sub-test 
    
         // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 3);
    
         // Do any necessary setup here for the 1st sub-test 
    
         // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 0);
     }
    
  3. [最佳技术] 将 InvokeWithoutArgs(f)local 计数器变量和一个简单的 lambda 函数一起使用! 这很好用,比第一种方法更容易使用和更通用,同时避免了第二种方法的全局变量和附加全局函数。这肯定是我最喜欢的处理方式,而且效果非常好。

    这项技术实际上已经过测试并证明适用于真实代码:

     using ::testing::_;
     using ::testing::InvokeWithoutArgs;
    
     TEST_F(MyTestFixture, MyCustomTest)
     {
         uint32_t callCounter;
    
         EXPECT_CALL(MyMockClass, myMockMethod(_, _))
             // Use a lambda function to set gmock to increment `callCounter` every 
             // time `myMockMethod()` is called with *any* input parameters!
             .WillRepeatedly(InvokeWithoutArgs([&callCounter](){ callCounter++; }));
    
         // Do any necessary setup here for the 1st sub-test 
    
         // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 2);
    
         // Do any necessary setup here for the 2nd sub-test 
    
         // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 3);
    
         // Do any necessary setup here for the 1st sub-test 
    
         // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
         // despite calling `myOtherFunc()` repeatedly
         callCounter = 0; // ensure this is zero BEFORE you start the test!
         for (int i = 0; i < 10; i++)
         {
             myOtherFunc();
         }
         EXPECT_EQ(callCounter, 0);
     }
    

如果您认为整个答案应该作为 stand-alone 文件添加到 Gmock 文档中(我建议我们这样做),请单击下方的 github 问题 link 并点个赞。

练习使用gtest/gmock:

  1. 使用此项目练习编写和测试您自己的 google 测试和 google 模拟。这也是一个很好的演示如何启动新项目和 运行 Google 的 Bazel build system: https://github.com/ElectricRCAircraftGuy/eRCaGuy_gtest_practice.

相关:

  1. GitHub issue I open to request to add this answer as a standalone document in their official documentation.如果您同意,请到这里为这个问题点赞:https://github.com/google/googletest/issues/2775
  2. Google Mock: multiple expectations on same function with different parameters
  3. Interleaving EXPECT_CALL()s and calls to the mock functions
    1. see my answer here