google mock分享(全网最全最好的gmock文档,没有之一)
Content |
---|
Google Mock 入门
概述
什么是Mock?
Mock,更确切地说应该是Mock Object。它究竟是什么?它有什么作用?在这里,我也只能先说说我的理解。 比如当我们在单元测试、模块的接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(那名开发同学比较懒,呵呵),这时我们就可以定义了Mock对象来模拟那些类的行为。
说得更直白一些,就是自己实现一个假的依赖类,对这个类的方法你想要什么行为就可以有什么行为,你想让这个方法返回什么结果就可以返回怎么样的结果。
但这时很多同学往往会提出一个问题:"那既然是我自己实现一个假的依赖类",那和那些市面上的Mock框架有什么关系啊?
这个其实是这样的,这些个Mock框架可以帮助你比较方便、比较轻松地实现这些个假的依赖类。毕竟,如果你实现这么一个假的依赖类的时间花费过场的话,那我还不如等待那位懒惰的同学吧。
Google Mock概述
Google Mock(简称gmock)是Google在2008年推出的一套针对C++的Mock框架,它灵感取自于jMock、EasyMock、harcreat。它提供了以下这些特性:
- 轻松地创建mock类
- 支持丰富的匹配器(Matcher)和行为(Action)
- 支持有序、无序、部分有序的期望行为的定义
- 多平台的支持
参考文档
Google Mock使用
最简单的例子
我比较喜欢举例来说明这些个、那些个玩意,因此我们先来看看Google Mock就简单的用法和作用。
- 首先,那个懒惰的同学已经定义好了这么一个接口(万幸,他至少把接口定义好了):
FooInterface.h
- #ifndef FOOINTERFACE_H_
- #define FOOINTERFACE_H_
- #include <string>
- namespace seamless {
- class FooInterface {
- public:
- virtual ~FooInterface() {}
- public:
- virtual std::string getArbitraryString() = 0;
- };
- } // namespace seamless
- #endif // FOOINTERFACE_H_
- FooInterface的析构函数~FooInterface()必须是virtual的
- 在第13行,我们得把getArbitraryString定义为纯虚函数。其实getArbitraryString()也不一定得是纯虚函数,这点我们后面会提到.
现在我们用Google Mock来定义Mock类 FooMock.h
- #ifndef MOCKFOO_H_
- #define MOCKFOO_H_
- #include <gmock/gmock.h>
- #include <string>
- #include "FooInterface.h"
- namespace seamless {
- class MockFoo: public FooInterface {
- public:
- MOCK_METHOD0(getArbitraryString, std::string());
- };
- } // namespace seamless
- #endif // MOCKFOO_H_
- 第10行我们的MockFoo类继承懒同学的FooInterface
- 第22行我们定义使用gmock中的一个宏(Macro)MOCK_METHOD0来定义MockFoo中的getArbitraryString。Google Mock是需要你根据不同的形参个数来使用不同的Mock Method,我这里getArbitraryString没有函数,就是MOCK_METHOD0了,同理,如果是一个形参,就是MOCK_METHOD1了,以此往下。
FooMain.cc
- #include <cstdlib>
- #include <gmock/gmock.h>
- #include <gtest/gtest.h>
- #include <iostream>
- #include <string>
- #include "MockFoo.h"
- using namespace seamless;
- using namespace std;
- using ::testing::Return;
- int main(int argc, char** argv) {
- ::testing::InitGoogleMock(&argc, argv);
- string value = "Hello World!";
- MockFoo mockFoo;
- EXPECT_CALL(mockFoo, getArbitraryString()).Times(1).
- WillOnce(Return(value));
- string returnValue = mockFoo.getArbitraryString();
- cout << "Returned Value: " << returnValue << endl;
- return EXIT_SUCCESS;
- }
最后我们运行编译,得到的结果如下:
- 第15行,初始化一个Google Mock
- 第18行,声明一个MockFoo的对象:mockFoo
- 第19行,是为MockFoo的getArbitraryString()方法定义一个期望行为,其中Times(1)的意思是运行一次,WillOnce(Return(value))的意思是第一次运行时把value作为getArbitraryString()方法的返回值。
这就是我们最简单的使用Google Mock的例子了,使用起来的确比较简便吧。
典型的流程
通过上述的例子,已经可以看出使用Mock类的一般流程如下:
- 引入你要用到的Google Mock名称. 除宏或其它特别提到的之外所有Google Mock名称都位于*testing*命名空间之下.
- 建立模拟对象(Mock Objects).
- 可选的,设置模拟对象的默认动作.
- 在模拟对象上设置你的预期(它们怎样被调用,应该怎样回应?).
自定义方法/成员函数的期望行为
从上述的例子中可以看出,当我们针对懒同学的接口定义好了Mock类后,在单元测试/主程序中使用这个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)的意思是缺省/重复行为。
我稍微先举个例子来说明一下,后面有针对更为详细的说明:
- 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
Matcher(匹配器)
Matcher用于定义Mock类中的方法的形参的值(当然,如果你的方法不需要形参时,可以保持match为空。),它有以下几种类型:(更详细的介绍可以参见Google Mock Wiki上的Matcher介绍)
通配符
_ | 可以代表任意类型 |
A |
可以是type类型的任意值 |
一般比较
Eq(value) 或者 value | argument == value,method中的形参必须是value |
Ge(value) | argument >= value,method中的形参必须大于等于value |
Gt(value) | argument > value |
Le(value) | argument <= value |
Lt(value) | argument < value |
Ne(value) | argument != value |
IsNull() | method的形参必须是NULL指针 |
NotNull() | argument is a non-null pointer |
Ref(variable) | 形参是variable的引用 |
TypedEq |
形参的类型必须是type类型,而且值必须是value |
浮点数的比较
DoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是不相等的 |
FloatEq(a_float) | 同上,只不过类型是float |
NanSensitiveDoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是相等的,这个是用户所希望的方式 |
NanSensitiveFloatEq(a_float) | 同上,只不过形参是float |
字符串匹配
这里的字符串即可以是C风格的字符串,也可以是C++风格的。
ContainsRegex(string) | 形参匹配给定的正则表达式 |
EndsWith(suffix) | 形参以suffix截尾 |
HasSubstr(string) | 形参有string这个子串 |
MatchesRegex(string) | 从第一个字符到最后一个字符都完全匹配给定的正则表达式. |
StartsWith(prefix) | 形参以prefix开始 |
StrCaseEq(string) | 参数等于string,并且忽略大小写 |
StrCaseNe(string) | 参数不是string,并且忽略大小写 |
StrEq(string) | 参数等于string |