添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接


文章目录

  • 一、如何使用gMock
  • 1.自定义方法/成员函数的期望行为
  • 2.匹配器
  • 3.基数Cardinalities
  • 4.行为(Actions)
  • 5.序列(Sequences)
  • 6.基于虚函数的Mock实战
  • 7.Nice Mocks 和 Strict Mocks
  • 8.ON_CALL与EXPECT_CALL区别
  • 二、实战
  • 1.Cardinalities: How Many Times Will It Be Called?
  • 2.Matchers: What Arguments Do We Expect?
  • 3.Actions: What Should It Do?
  • 4.Using Multiple Expectations
  • 5.Ordered vs Unordered Calls
  • 6.All Expectations Are Sticky
  • 三、gMock Cookbook
  • 1.Creating Mock Classes
  • 2.Mocking Private or Protected Methods
  • 3.Mocking Overloaded Methods
  • 4.Mocking Class Templates
  • 5.Mocking Non-virtual Methods
  • 6.Mocking Free Functions
  • 7.Old-Style MOCK_METHODn Macros

一、如何使用gMock

1 首先,使用一些简单的宏来描述要模拟的接口,它们将扩展到模拟类的实现;

2 接下来,创建一些模拟对象,并使用直观的语法指定它的期望和行为;

3.然后练习使用模拟对象的代码。一旦出现任何违反预期的情况,gMock将立即捕获。

mock的语法见:docs/reference/mocking.md

1.自定义方法/成员函数的期望行为

  • ref:转一篇小亮同学的google mock分享

对方法期望行为的定义的语法格式如下:

EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
    .With(multi_argument_matcher)
    .Times(cardinality)
    .InSequence(sequences)
    .After(expectations)
    .WillOnce(action)
    .WillRepeatedly(action)
    .RetiresOnSaturation();
第1行的mock_object就是你的Mock类的对象
第1行的method(matcher1, matcher2, …)中的method就是你Mock类中的某个方法名,比如上述的getArbitraryString;而matcher(匹配器)的意思是定义方法参数的类型,我们待会详细介绍。
第3行的Times(cardinality)的意思是之前定义的method运行几次。至于cardinality的定义,我也会在后面详细介绍。
第4行的InSequence(sequences)的意思是定义这个方法被执行顺序(优先级),我会再后面举例说明。
第6行WillOnce(action)是定义一次调用时所产生的行为,比如定义该方法返回怎么样的值等等。
第7行WillRepeatedly(action)的意思是缺省/重复行为。
  • eg:
EXPECT_CALL(mockTurtle, getX()).Times(testing::AtLeast(5)).
                WillOnce(testing::Return(100)).WillOnce(testing::Return(150)).
                WillRepeatedly(testing::Return(200))
调用mockTurtle的getX()方法
这个方法会至少调用5次
第一次被调用时返回100
第2次被调用时返回150
从第3次被调用开始每次都返回200

2.匹配器

Matcher用于定义Mock类中的方法的形参的值(当然,如果你的方法不需要形参时,可以保持match为空。),它有以下几种类型

通配符

  • 这里的_和*A*包括下面的那个匹配符都在Google Mock的*::testing*这个命名空间下,大家要用时需要先引入那个命名空间

一般比较

Gtest/Gmock实战之Gmock_ide


浮点数的比较

Gtest/Gmock实战之Gmock_ide_02

字符串匹配

  • 这里的字符串即可以是C风格的字符串,也可以是C++风格的。

容器的匹配

  • 很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(container)来比较。但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:
  • eg:
#ifndef MOCKFOO_H_
#define MOCKFOO_H_
#include <gmock/gmock.h>
#include <string>
#include <vector>
#include "FooInterface.h"
namespace seamless {
class MockFoo: public FooInterface {
public:
        MOCK_METHOD(std::string, getArbitraryString, ());
        MOCK_METHOD1(void, setValue, (std::string& value));
        MOCK_METHOD2(void, setDoubleValues, (int x, int y));
}  // namespace seamless
#endif // MOCKFOO_H_
#include <cstdlib>
#include <gmock/gmock.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
using ::testing::Assign;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Return;
int main(int argc, char** argv) {
        ::testing::InitGoogleMock(&argc, argv);
        string value = "Hello World!";
        MockFoo mockFoo;
        EXPECT_CALL(mockFoo, setValue(testing::_));
        mockFoo.setValue(value);
        // 这里我故意犯错
        EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)));
        mockFoo.setDoubleValues(1, 0);
        return EXIT_SUCCESS;
}

第22行,让setValue的形参可以传入任意参数
另外,我在第26~27行故意犯了个错(为了说明上述这些匹配器的作用),我之前明明让setDoubleValues第二个参数得大于等于1,但我实际传入时却传入一个0。这时程序运行时就报错了:

unknown file: Failure
Unexpected mock function call – returning directly.
Function call: setDoubleValues(1, 0)
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:35: EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)))…
Expected arg #1: is >= 1
Actual: 0
Expected: to be called once
Actual: never called – unsatisfied and active
FooMain.cc:35: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)))…
Expected: to be called once
Actual: never called – unsatisfied and active

成员匹配器

Gtest/Gmock实战之Gmock_c++_03

  • eg:第5行,我们定义了一个Field(&Bar::num, Ge(0)),以说明Bar的成员变量num必须大于等于0。
TEST(TestField, Simple) {
        MockFoo mockFoo;
        Bar bar;
        EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
        mockFoo.get(bar);
int main(int argc, char** argv) {
        ::testing::InitGoogleMock(&argc, argv);
        return RUN_ALL_TESTS();
}
  • eg:我们为了说明Field的作用,传入一个bar.num = -1试试。
TEST(TestField, Simple) {
        MockFoo mockFoo;
        Bar bar;
        bar.num = -1;
        EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
        mockFoo.get(bar);
}
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from TestField
[ RUN ] TestField.Simple
unknown file: Failure
Unexpected mock function call – returning directly.
Function call: get(@0xbff335bc 4-byte object )
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:34: EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0))))…
Expected arg #0: is an object whose given field is >= 0
Actual: 4-byte object , whose given field is -1
Expected: to be called once
Actual: never called – unsatisfied and active
FooMain.cc:34: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0))))…
Expected: to be called once
Actual: never called – unsatisfied and active
[ FAILED ] TestField.Simple (0 ms)
[----------] 1 test from TestField (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] TestField.Simple
1 FAILED TEST

匹配函数或函数对象的返回值

Gtest/Gmock实战之Gmock_ide_04

指针匹配器

Gtest/Gmock实战之Gmock_ide_05

复合匹配器

Gtest/Gmock实战之Gmock_#include_06

  • eg:传入的参数必须 >5 并且 <= 10
EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));
  • eg:第一个参数不包含“blah”这个子串
EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));

3.基数Cardinalities

基数用于Times()中来指定模拟函数将被调用多少次

Gtest/Gmock实战之Gmock_c++_07

4.行为(Actions)

Actions(行为)用于指定Mock类的方法所期望模拟的行为:比如返回什么样的值、对引用、指针赋上怎么样个值,等等。

  • 值的返回
  • 副作用(Side Effects)

Gtest/Gmock实战之Gmock_ide_08

  • 使用函数或者函数对象(Functor)作为行为
  • 复合动作
  • eg:例如有一个Mock方法:
irtual int getParamter(std::string* name,  std::string* value) = 0

对于这个方法,我这回需要操作的结果是将name指向value的地址,并且得到方法的返回值。
类似这样的需求,我们就可以这样定义期望过程:

  • 这时就用上了我们的DoAll()了,它将Assign()和Return()结合起来了。
TEST(SimpleTest, F1) {
    std::string* a = new std::string("yes");
    std::string* b = new std::string("hello");
    MockIParameter mockIParameter;
    EXPECT_CALL(mockIParameter, getParamter(testing::_, testing::_)).Times(1).\
        WillOnce(testing::DoAll(testing::Assign(&a, b), testing::Return(1)));
    mockIParameter.getParamter(a, b);
}

5.序列(Sequences)

默认时,对于定义要的期望行为是无序(Unordered)的,即当我定义好了如下的期望行为:

  • eg:对于这样的期望行为的定义,我何时调用mockFoo.getValue()或者何时mockFoo.getSize()都可以的。
MockFoo mockFoo;
        EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
        EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));

但有时候我们需要定义有序的(Ordered)的调用方式,即序列 (Sequences) 指定预期的顺序. 在同一序列里的所有预期调用必须按它们指定的顺序发生; 反之则可以是任意顺序.

using ::testing::Return;
using ::testing::Sequence;
int main(int argc, char **argv) {
        ::testing::InitGoogleMock(&argc, argv);
        Sequence s1, s2;
        MockFoo mockFoo;
        EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2).WillOnce(Return(1));
        EXPECT_CALL(mockFoo, getValue()).InSequence(s1).WillOnce(Return(
                string("Hello World!")));
        cout << "First:\t" << mockFoo.getSize() << endl;
        cout << "Second:\t" << mockFoo.getValue() << endl;
        return EXIT_SUCCESS;
}
  • 首先在第8行建立两个序列:s1、s2。
  • 然后在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)说明getSize()的行为优先于s1、s2.
  • 而第12行时,EXPECT_CALL(mockFoo, getValue()).InSequence(s1)说明getValue()的行为在序列s1- 中。
  • 结果:
First: 1
Second: Hello World!
  • eg:把mockFoo.getSize()和mockFoo.getValue()的调用对调时试试:
cout << "Second:\t" << mockFoo.getValue() << endl;
        cout << "First:\t" << mockFoo.getSize() << endl;
unknown file: Failure
Unexpected mock function call – returning default value.
Function call: getValue()
Returns: ""
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:29: EXPECT_CALL(mockFoo, getValue())…
Expected: all pre-requisites are satisfied
Actual: the following immediate pre-requisites are not satisfied:
FooMain.cc:28: pre-requisite #0
(end of pre-requisites)
Expected: to be called once
Actual: never called – unsatisfied and active
Second:
First: 1
FooMain.cc:29: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, getValue())…
Expected: to be called once
Actual: never called – unsatisfied and active

另外,我们还有一个偷懒的方法,就是不要这么傻乎乎地定义这些个Sequence s1, s2的序列,而根据我定义期望行为(EXPECT_CALL)的顺序而自动地识别调用顺序,这种方式可能更为地通用。

using ::testing::InSequence;
using ::testing::Return;
int main(int argc, char **argv) {
        ::testing::InitGoogleMock(&argc, argv);
        InSequence dummy;
        MockFoo mockFoo;
        EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
        EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
        cout << "First:\t" << mockFoo.getSize() << endl;
        cout << "Second:\t" << mockFoo.getValue() << endl;
        return EXIT_SUCCESS;
}

6.基于虚函数的Mock实战

VariantField.h

#ifndef VARIANTFIELD_H_
#define VARIANTFIELD_H_
#include <boost/cstdint.hpp>
namespace seamless
    union VariantField
        const char *strVal;
        int32_t intVal;
} // namespace mlr_isearch_api
#endif // VARIANTFIELD_H_

IAPIProviderInterface.h

#ifndef IAPIPROVIDERINTERFACE_H_
#define IAPIPROVIDERINTERFACE_H_
#include <boost/cstdint.hpp>
#include "IParameterInterface.h"
#include "VariantField.h"
namespace seamless
    class IAPIProviderInterface
    public:
        IAPIProviderInterface() {}
        virtual ~IAPIProviderInterface() {}
    public:
        virtual IParameterInterface *getParameterInterface() = 0;
#endif // IAPIPROVIDERINTERFACE_H_

IParameterInterface.h

#ifndef IPARAMETERINTERFACE_H_
#define IPARAMETERINTERFACE_H_
#include <boost/cstdint.hpp>
#include "VariantField.h"
namespace seamless
    class IParameterInterface
    public:
        virtual ~IParameterInterface(){};
    public:
        virtual int32_t getParameter(const char *name, VariantField *&value) = 0;
} // namespace
#endif // IPARAMETERINTERFACE_H_

MockIAPIProviderInterface.h

#ifndef MOCKIAPIPROVIDERINTERFACE_H_
#define MOCKIAPIPROVIDERINTERFACE_H_
#include <gmock/gmock.h>
#include "IAPIProviderInterface.h"
#include "IParameterInterface.h"
namespace seamless
    class MockIAPIProviderInterface : public IAPIProviderInterface
    public:
        MOCK_METHOD(IParameterInterface *, getParameterInterface, ());
} // namespace seamless
#endif // MOCKIAPIPROVIDERINTERFACE_H_

MockIParameterInterface.h

#ifndef MOCKIPARAMETERINTERFACE_H_
#define MOCKIPARAMETERINTERFACE_H_
#include <boost/cstdint.hpp>
#include <gmock/gmock.h>
#include "IParameterInterface.h"
#include "VariantField.h"
namespace seamless
    class MockIParameterInterface : public IParameterInterface
    public:
        MOCK_METHOD(int32_t, getParameter, (const char *name, VariantField *&value));
} // namespace seamless
#endif // MOCKIPARAMETERINTERFACE_H_

Rank.cc

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include "IAPIProviderInterface.h"
#include "IParameterInterface.h"
#include "VariantField.h"
#include "Rank.h"
using namespace seamless;
using namespace std;
namespace seamless
    void Rank::processQuery(IAPIProviderInterface *iAPIProvider)
        IParameterInterface *iParameter = iAPIProvider->getParameterInterface();
        if (!iParameter)
            cerr << "iParameter is NULL" << endl;
            return;
        int32_t isRetailWholesale = 0;
        int32_t isUseAlipay = 0;
        VariantField *value = new VariantField;
        iParameter->getParameter("retail_wholesale", value);
        isRetailWholesale = (strcmp(value->strVal, "0")) ? 1 : 0;
        iParameter->getParameter("is_use_alipay", value);
        isUseAlipay = (strcmp(value->strVal, "0")) ? 1 : 0;
        cout << "isRetailWholesale:\t" << isRetailWholesale << endl;
        cout << "isUseAlipay:\t" << isUseAlipay << endl;
        delete value;
        delete iParameter;
} // namespace seamless

Rank.h

#ifndef RANK_H_
#define RANK_H_
#include "IAPIProviderInterface.h"
namespace seamless
    class Rank
    public:
        virtual ~Rank() {}
    public:
        void processQuery(IAPIProviderInterface *iAPIProvider);
} // namespace seamless
#endif // RANK_H_

tester.cc

#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <cstdlib>
#include <gmock/gmock.h>
#include "MockIAPIProviderInterface.h"
#include "MockIParameterInterface.h"
#include "Rank.h"
using namespace seamless;
using namespace std;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SetArgumentPointee;
int main(int argc, char **argv)
    ::testing::InitGoogleMock(&argc, argv);
    MockIAPIProviderInterface *iAPIProvider = new MockIAPIProviderInterface;
    MockIParameterInterface *iParameter = new MockIParameterInterface;
    // 定义MockIAPIProviderInterface.getParameterInterface的的行为:程序至少被调用一次(Times(AtLeast(1))),每次调用都返回一个iParameter(即MockIParameterInterface*的对象)。
    EXPECT_CALL(*iAPIProvider, getParameterInterface()).Times(AtLeast(1)).WillRepeatedly(Return(iParameter));
    // 假设了一些Query的Segment的值
    boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
    retailWholesaleValue->strVal = "0";
    boost::shared_ptr<VariantField> defaultValue(new VariantField);
    defaultValue->strVal = "9";
    // 定义MockIParameterInterface.getParameter的期望行为:这个方法至少被调用一次;第一次被调用时返回1并将第一个形参指向retailWholesaleValue;后续几次被调用时返回1,并指向defaultValue。
    EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
    // 运行Rank类下的processQuery方法。
    Rank rank;
    rank.processQuery(iAPIProvider);
    delete iAPIProvider;
    return EXIT_SUCCESS;
}

7.Nice Mocks 和 Strict Mocks

Foo.h 带private方法的接口

class Foo {
private:
        virtual void setValue(int value) {};
public:
        int value;
};

MockFoo.h

class MockFoo: public Foo {
public:
        MOCK_METHOD1(setValue, void(int value));
};

当在调用Mock类的方法时,如果之前没有使用EXPECT_CALL来定义该方法的期望行为时,Google Mock在运行时会给你一些警告信息:

  • 一般用不上
GMOCK WARNING:
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0
Stack trace

对于这种情况,可以使用NiceMock来避免:

  • 使用NiceMock来替代之前的MockFoo。
using ::testing::NiceMock;
using ::testing::NaggyMock;
using ::testing::StrictMock;
        // MockFoo mockFoo;
        NiceMock<MockFoo> mockFoo;

当然,另外还有一种办法,就是使用StrictMock来将这些调用都标为失败:

StrictMock<MockFoo> mockFoo;

这时得到的结果:

unknown file: Failure
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0

8.ON_CALL与EXPECT_CALL区别

  • ref:gmock_faq.md

如果你确定这些调用是正确的。这将告诉gMock您确实期望调用,并且不应该打印任何警告。

  • 写法不同
  • The mock function has no default action set, and its return type has no default value set,则使用ON_CALL
using ::testing::_;
  EXPECT_CALL(foo, Bar(_))
      .WillRepeatedly(...);

instead of

using ::testing::_;
  ON_CALL(foo, Bar(_))
      .WillByDefault(...);

二、实战

Turtle 接口定义

class Turtle {
  virtual ~Turtle() {}
  virtual void PenUp() = 0;
  virtual void PenDown() = 0;
  virtual void Forward(int distance) = 0;
  virtual void Turn(int degrees) = 0;
  virtual void GoTo(int x, int y) = 0;
  virtual int GetX() const = 0;
  virtual int GetY() const = 0;
};

编写Mock类

#include "gmock/gmock.h"  // Brings in gMock.
class MockTurtle : public Turtle {
 public:
  MOCK_METHOD(void, PenUp, (), (override));
  MOCK_METHOD(void, PenDown, (), (override));
  MOCK_METHOD(void, Forward, (int distance), (override));
  MOCK_METHOD(void, Turn, (int degrees), (override));
  MOCK_METHOD(void, GoTo, (int x, int y), (override));
  MOCK_METHOD(int, GetX, (), (const, override));
  MOCK_METHOD(int, GetY, (), (const, override));
};

使用Mock

·1从测试名称空间导入gMock名称,以便可以不限定地使用它们(每个文件只需执行一次)。记住,名称空间是个好主意。
2.创建一些模拟对象。
3.指定您对它们的期望(一个方法将被调用多少次?用什么参数?它应该做什么?等等)。
4.练习一些使用模拟的代码;可选地,使用检查结果
5.googletest断言。如果一个模拟方法被调用的次数超过预期,或者使用了错误的参数,您将立即得到一个错误。
6.当一个mock被销毁时,gMock将自动检查对它的所有期望是否已得到满足。

```cpp
#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::AtLeast;                         // #1
TEST(PainterTest, CanDrawSomething) {
  MockTurtle turtle;                              // #2
  EXPECT_CALL(turtle, PenDown())                  // #3
      .Times(AtLeast(1));
  Painter painter(&turtle);                       // #4
  EXPECT_TRUE(painter.DrawCircle(0, 0, 10));      // #5
}

这个测试检查至少调用了一次PenDown()。如果painter对象没有调用这个方法,你的测试就会失败,并显示如下信息:

path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.
Stack trace:
...

给Mock设置EXPECT_CALL

宏有两个参数:第一个是模拟对象,然后是方法及其参数参数。注意,两者之间用逗号(,)隔开,而不是句号(.)。如果方法没有重载,宏也可以在没有匹配器的情况下被调用
在gMock中,我们使用EXPECT_CALL()宏来设置模拟方法的期望。一般语法是:

1.Cardinalities: How Many Times Will It Be Called?

在EXPECT_CALL()之后可以指定的第一个子句是Times()。我们将其参数称为基数,因为它告诉调用应该进行多少次

/*
Cardinalities:即.Times()
Times()子句可以省略。如果省略Times(), gMock将推断你的基数。
如果WillOnce()和willrepeated()都不在, EXPECT_CALL(),推断的基数是Times(1)。
如果有n个WillOnce()但没有willrepeated(),其中n >=1,基数为Times(n)。
如果有n个WillOnce()和一个willrepeated(),其中n >=0,基数为Times(AtLeast(n))。
EXPECT_CALL(mock_object, method(matchers))
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action);
EXPECT_CALL(mock_object, non-overloaded-method)
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action);
它表示turtle对象的GetX()方法将被调用五次第一次返回100,第二次返回150,然后每次返回200。*/
EXPECT_CALL(turtle, GetX())
    .Times(5)
    .WillOnce(Return(100))
    .WillOnce(Return(150))
    .WillRepeatedly(Return(200));

2.Matchers: What Arguments Do We Expect?

当mock函数接受实参时,我们可以指定期望的实参

补充:非重载方法的Mock,若下面的接口不是virtual的

// Expects the turtle to move forward by 100 units.
EXPECT_CALL(turtle, Forward(100));
_ as the argument, which means "anything goes":
// Expects that the turtle jumps to somewhere on the x=50 line.
EXPECT_CALL(turtle, GoTo(50, _));
// Expects the turtle moves forward by at least 100.
Ge是Gtest内置的Matchers
Matchers:reference/matchers.md
EXPECT_CALL(turtle, Forward(Ge(100)));
//如果你不关心任何参数,而不是为每个参数指定_,你可以省略形参列表:
// Expects the turtle to move forward.
EXPECT_CALL(turtle, Forward);
// Expects the turtle to jump somewhere.
EXPECT_CALL(turtle, GoTo);

3.Actions: What Should It Do?

首先,如果模拟函数的返回类型是内置类型或指针,该函数有一个默认动作(void函数将返回bool函数返回false,其他函数返回0),在c++ 11及以上版本中,一个模拟函数,其返回类型为default-constructible(即有一个默认构造函数)的默认动作为返回默认构造的值。如果你什么都不说,这种行为将被使用。

其次,如果模拟函数没有默认动作,或者默认动作不适合你,你可以指定每次要采取的行动使用一系列WillOnce()子句后跟一个可选WillRepeatedly ()

ReturnRef(variable)可以返回引用

/*
我们没有显式地编写Times()),并将分别返回100、200和300
using ::testing::Return;
EXPECT_CALL(turtle, GetX())
     .WillOnce(Return(100))
     .WillOnce(Return(200))
     .WillOnce(Return(300));
没有explicit Times()),前两次将分别返回100和200,从第三次开始是300。
EXPECT_CALL(turtle, GetY())
     .WillOnce(Return(100))
     .WillOnce(Return(200))
     .WillRepeatedly(Return(300));

注意:EXPECT_CALL()语句只计算action子句一次,即使该操作可能被执行多次。所以你一定要注意副作用。

using ::testing::Return;
int n = 100;
EXPECT_CALL(turtle, GetX())
    .Times(4)
    .WillRepeatedly(Return(n++));

这里不是返回100 101 102,…连续地,这个模拟函数会总是返回100,就像n + +只被评估过一次

using ::testing::Return;
EXPECT_CALL(turtle, GetY())
    .Times(4)
    .WillOnce(Return(100));

记住,每次调用函数时都会使用WillOnce()子句,然后执行默认操作。因此正确答案是turtle.GetY()第一次返回100,但第二次返回0,因为返回0是int函数的默认操作。

4.Using Multiple Expectations

默认情况下,当调用mock方法时,gMock将按照定义期望的相反顺序搜索期望,并在找到与参数匹配的活动期望时停止(您可以将其理解为“更新的规则覆盖旧的。”)。

using ::testing::_;
EXPECT_CALL(turtle, Forward(_));  // #1
EXPECT_CALL(turtle, Forward(10))  // #2
    .Times(2);

如果Forward(10)连续被调用三次,那么第三次它将是错误,因为最后一个匹配期望(#2)已经饱和。然而,如果第三次Forward(10)调用被Forward(20)取代,那么它就OK了,现在第一个是匹配的期望。

5.Ordered vs Unordered Calls

在这个例子中,我们测试Foo()调用对象中的三个预期函数按写好的顺序。如果一个调用是无序的,它将是一个错误。

using ::testing::InSequence;
TEST(FooTest, DrawsLineSegment) {
    InSequence seq;
    EXPECT_CALL(turtle, PenDown());
    EXPECT_CALL(turtle, Forward(100));
    EXPECT_CALL(turtle, PenUp());
  Foo();
}

6.All Expectations Are Sticky

gMock中的期望在默认情况下是“粘性的”,即使在我们达到它们的调用上限之后,它们仍然是活动的。这是一个需要记住的重要规则,因为它影响到规范的含义.

假设turtle.GoTo(0,0)被调用三次。在第三次中,gMock将执行确保参数与期望#2匹配(请记住,我们总是选择最后匹配期望)。既然我们说过只有两个这样的调用,gMock会立即报告错误。

using ::testing::_;
using ::testing::AnyNumber;
EXPECT_CALL(turtle, GoTo(_, _))  // #1
     .Times(AnyNumber());
EXPECT_CALL(turtle, GoTo(0, 0))  // #2
     .Times(2);

如果你认为它说turtle.GetX()将被调用n次并返回10,20,30,…,连续,三思!问题是,正如我们所说,期望是有粘性的。因此,第二次调用turtle.GetX()时,最后(最新)EXPECT_CALL()语句将匹配,并立即导致“违反上限”错误

using ::testing::Return;
 for (int i = 1; i <= n; i++) {
  EXPECT_CALL(turtle, GetX())
      .WillOnce(Return(10*i));
}

正确做法如下:
正确的说法是turtle.GetX()将返回10,20,30,…就是明确地说期望是没有粘性的。换句话说,他们应该在饱和后立即退休:

using ::testing::Return;
 for (int i = 1; i <= n; i++) {
  EXPECT_CALL(turtle, GetX())
      .WillOnce(Return(10*i))
      .RetiresOnSaturation();
}

三、gMock Cookbook

1.Creating Mock Classes

class MyMock {
 public:
  MOCK_METHOD(ReturnType, MethodName, (Args...));
  MOCK_METHOD(ReturnType, MethodName, (Args...), (Specs...));
};

前3个参数只是方法声明,分为3部分。第四个参数接受限定符的封闭列表,该限定符将影响生成的方法:

const -使被模拟的方法为const方法。如果需要重写const方法。
override -用override标记方法。如果覆盖,建议使用虚方法。
noexcept -用noexcept标记方法。如果重写noexcept方法。
Calltype(…)-设置方法的调用类型(例如:为STDMETHODCALLTYPE),在Windows中很有用。
ref(…)-用引用限定符标记方法指定。如果重写具有引用的方法则必须资格。例如ref(&)或ref(&&)。

  • 参数里面有逗号,需要用()括起来
class MockFoo {
 public:
  MOCK_METHOD(std::pair<bool, int>, GetPair, ());  // Won't compile!
  MOCK_METHOD(bool, CheckMap, (std::map<int, double>, bool));  // Won't compile!
class MockFoo {
 public:
  MOCK_METHOD((std::pair<bool, int>), GetPair, ());
  MOCK_METHOD(bool, CheckMap, ((std::map<int, double>), bool));
class MockFoo {
 public:
  using BoolAndInt = std::pair<bool, int>;
  MOCK_METHOD(BoolAndInt, GetPair, ());
  using MapIntDouble = std::map<int, double>;
  MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool));
};

2.Mocking Private or Protected Methods

你必须总是把一个模拟方法定义(MOCK_METHOD)放在模拟类的public:部分中,不管被模拟的方法是公共的,基类中的Protected或private。这允许ON_CALL和EXPECT_CALL从模拟类外部引用模拟函数。

(是的,c++允许子类改变基类中虚函数的访问级别。)

class Foo {
 public:
  virtual bool Transform(Gadget* g) = 0;
 protected:
  virtual void Resume();
 private:
  virtual int GetTimeOut();
class MockFoo : public Foo {
 public:
  MOCK_METHOD(bool, Transform, (Gadget* g), (override));
  // The following must be in the public section, even though the
  // methods are protected or private in the base class.
  MOCK_METHOD(void, Resume, (), (override));
  MOCK_METHOD(int, GetTimeOut, (), (override));
};

3.Mocking Overloaded Methods

class Foo {
  // Must be virtual as we'll inherit from Foo.
  virtual ~Foo();
  // Overloaded on the types and/or numbers of arguments.
  virtual int Add(Element x);
  virtual int Add(int times, Element x);
  // Overloaded on the const-ness of this object.
  virtual Bar& GetBar();
  virtual const Bar& GetBar() const;
class MockFoo : public Foo {
  MOCK_METHOD(int, Add, (Element x), (override));
  MOCK_METHOD(int, Add, (int times, Element x), (override));
  MOCK_METHOD(Bar&, GetBar, (), (override));
  MOCK_METHOD(const Bar&, GetBar, (), (const, override));
};

如果您没有模拟重载方法的所有版本,编译器将警告基类中的某些方法被隐藏。要解决这个问题,请使用using将它们引入范围:

class MockFoo : public Foo {
  using Foo::Add;
  MOCK_METHOD(int, Add, (Element x), (override));
  // We don't want to mock int Add(int times, Element x);
};

4.Mocking Class Templates

template <typename Elem>
class StackInterface {
  // Must be virtual as we'll inherit from StackInterface.
  virtual ~StackInterface();
  virtual int GetSize() const = 0;
  virtual void Push(const Elem& x) = 0;
template <typename Elem>
class MockStack : public StackInterface<Elem> {
  MOCK_METHOD(int, GetSize, (), (override));
  MOCK_METHOD(void, Push, (const Elem& x), (override));
};

5.Mocking Non-virtual Methods

在这种情况下,模拟类不会与真实类共享一个公共基类,而是与真实类无关,但包含具有相同签名的方法。模拟非虚方法的语法与模拟虚拟方法(只是不添加override):

// A simple packet stream class.  None of its members is virtual.
class ConcretePacketStream {
 public:
  void AppendPacket(Packet* new_packet);
  const Packet* GetPacket(size_t packet_number) const;
  size_t NumberOfPackets() const;
// A mock packet stream class.  It inherits from no other, but defines
// GetPacket() and NumberOfPackets().
class MockPacketStream {
 public:
  MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const));
  MOCK_METHOD(size_t, NumberOfPackets, (), (const));
};

一种方法是对需要使用包流的代码进行模板化。更具体地说,您将为您的代码提供一个数据包流类型的模板类型参数。在生产中,您将使用实例化模板ConcretePacketStream作为类型参数。在测试中,您将使用MockPacketStream实例化相同的模板。例如,你可以这样写:

template <class PacketStream>
void CreateConnection(PacketStream* stream) { ... }
template <class PacketStream>
class PacketReader {
 public:
  void ReadPackets(PacketStream* stream, size_t packet_num);
};

然后在生产代码中使用CreateConnection()和PacketReader,在生产代码中使用CreateConnection()和PacketReader测试。

MockPacketStream mock_stream;
  EXPECT_CALL(mock_stream, ...)...;
  .. set more expectations on mock_stream ...
  PacketReader<MockPacketStream> reader(&mock_stream);
  ... exercise reader ...

依赖注入的eg:

  • ref:mock non-virtual methods class A{ public:   int Funtion1(B& obj) {     //do something     std::string str = “mock non-virtual methods using templates”;      auto rst = obj.Function2(str);     //do something class B{ public: int Funtion2(std::string _str){ puts(_str.c_str()); } }

当我们对类A的方法Function1进行UT防护的时候,不关心其中类B的方法Function2的执行结果,这时候该如何对其进行mock呢(Function2是非虚的)?
Gtest提供了依赖注入的方式

将类A修改为:
template <class T1 >
class  RefactorA{
public:
  int Funtion1(T1 & obj) {
    //do something
    std::string str = “mock non-virtual methods using templates”;
    auto rst = obj.Function2(str);
    //do something
}

重构之后,类RefactorA变成了类模板,在实例化的时候把依赖的类B显式的“注入”进去,这时候进行UT的时候,就可以把“注入”的类B的方法Function2 进行mock,代码如下:

对类A进行UT测试

class  MockB
public:
	MOCK_METHOD(int , Funtion2, (std::string))
class RefactorA _UT : public :: testing::Test
protected:
  virtual void SetUp(){}
  virtual void TearDown(){}
  RefactorA < MockB> mockObjA;//实例化模板类
TEST_F(RefactorA _UT , Funtion1)
  //期望类B的方法Function2被调用至少一次,返回值为100,参数为任意字符串
  MockB mockObjB;
  EXPECT_CALL(mockObjB, Funtion2 (_))
  .Times(AtLeast(1))
  .WillOnce(Return(100));
  auto  rst  =  mockObjA.Function1( mockObjB );//注意这里传入的是mock出来的对象 
  EXPECT_TRUE( rst );
}

6.Mocking Free Functions

不可能直接模拟一个自由函数(即c风格的函数或静态方法)。如果需要,可以重写代码来使用接口(抽象类)。

class FileInterface {
 public:
  virtual bool Open(const char* path, const char* mode) = 0;
class File : public FileInterface {
 public:
  bool Open(const char* path, const char* mode) override {
     return OpenFile(path, mode);
};

不要直接调用一个自由函数(比如OpenFile),而是为它引入一个接口,并有一个具体的子类来调用自由函数:
这可能看起来很麻烦,但在实践中,您经常有多个相关的函数可以放在同一个接口中,因此每个函数语法开销将大大降低。

7.Old-Style MOCK_METHODn Macros

Simple

Old

MOCK_METHOD1(Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int))

Const Method

Old

MOCK_CONST_METHOD1(Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (const))

Method in a Class Template

Old

MOCK_METHOD1_T(Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int))

Const Method in a Class Template

Old

MOCK_CONST_METHOD1_T(Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (const))

Method with Call Type

Old

MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))

Const Method with Call Type

Old

MOCK_CONST_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))

Method with Call Type in a Class Template

Old

MOCK_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (Calltype(STDMETHODCALLTYPE)))

Const Method with Call Type in a Class Template

Old

MOCK_CONST_METHOD1_T_WITH_CALLTYPE(STDMETHODCALLTYPE, Foo, bool(int))

New

MOCK_METHOD(bool, Foo, (int), (const, Calltype(STDMETHODCALLTYPE)))