https://www.cnblogs.com/welkinwalker/archive/2011/11/29/2267225.html
Defining a Mock Class
Google Mock 入门
概述
什么是Mock?
Mock,更确切地说应该是Mock Object。它究竟是什么?它有什么作用?在这里,我也只能先说说我的理解。 比如当我们在单元测试、模块的接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(那名开发同学比较懒,呵呵),这时我们就可以定义了Mock对象来模拟那些类的行为。
说得更直白一些,就是自己实现一个假的依赖类,对这个类的方法你想要什么行为就可以有什么行为,你想让这个方法返回什么结果就可以返回怎么样的结果。
但这时很多同学往往会提出一个问题:”那既然是我自己实现一个假的依赖类”,那和那些市面上的Mock框架有什么关系啊?
这个其实是这样的,这些个Mock框架可以帮助你比较方便、比较轻松地实现这些个假的依赖类。毕竟,如果你实现这么一个假的依赖类的时间花费过场的话,那我还不如等待那位懒惰的同学吧。
Google Mock概述
Google Mock(简称gmock)是Google在2008年推出的一套针对C++的Mock框架,它灵感取自于jMock、EasyMock、harcreat。它提供了以下这些特性:
Google Mock使用
最简单的例子
我比较喜欢举例来说明这些个、那些个玩意,因此我们先来看看Google Mock就简单的用法和作用。
include
namespace seamless {
class FooInterface { public: virtual ~FooInterface() {}
public: virtual std::string getArbitraryString() = 0; };
} // namespace seamless
endif // FOOINTERFACEH
> 这里需要注意几点> - `FooInterface`的析构函数`~FooInterface()`必须是`virtual`的> - 在第15行,我们得把`getArbitraryString`定义为纯虚函数。其实`getArbitraryString()`也不一定得是纯虚函数,这点我们后面会提到.现在我们用Google Mock来定义`Mock`类```cpp#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_
我们稍微来解释一下这个Mock类的定义:
- 第10行我们的
MockFoo类继承懒同学的FooInterface - 第12行我们定义使用
gmock中的一个宏(Macro)MOCK_METHOD0来定义MockFoo中的getArbitraryString
Google Mock是需要你根据不同的形参个数来使用不同的Mock Method,
- 这里
getArbitraryString没有函数,就是MOCK_METHOD0了 - 同理,如果是一个形参,就是
MOCK_METHOD1了 - 以此往下。
// #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;Wint 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()方法的返回值。
最后我们运行编译,得到的结果如下:
Returned Value: Hello World!
典型的流程
通过上述的例子,已经可以看出使用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次 - 第
1次被调用时返回100 - 第
2次被调用时返回150 - 从第
3次被调用开始每次都返回200
Matcher(匹配器)
Matcher用于定义Mock类中的方法的形参的值(当然,如果你的方法不需要形参时,可以保持match为空。),它有以下几种类型:(更详细的介绍可以参见Google Mock Wiki上的Matcher介绍)
通配符
| _ | 可以代表任意类型 |
|---|---|
| A() or An() | 可以是type类型的任意值 |
这里的
_和A包括下面的那个匹配符都在Google Mock的::testing这个命名空间下,大家要用时需要先引入那个命名空间
一般比较
| 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(value) | 形参的类型必须是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 |
| StrNe(string) | 参数不等于string |
容器的匹配
很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(container)来比较。
但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:
| Contains(e) | 在method的形参中,只要有其中一个元素等于e |
|---|---|
| Each(e) | 参数各个元素都等于e |
| ElementsAre(e0, e1, …, en) | 形参有n+1的元素,并且挨个匹配 |
| ElementsAreArray(array) 或者ElementsAreArray(array, count) | 和ElementsAre()类似,除了预期值/匹配器来源于一个C风格数组 |
| ContainerEq(container) | 类型Eq(container),就是输出结果有点不一样,这里输出结果会带上哪些个元素不被包含在另一个容器中 |
| Pointwise(m, container) |
上述的一些匹配器都比较简单,我就随便打包举几最简单的例子演示一下吧: 我稍微修改一下之前的Foo.h和MockFoo.h, MockFoo.h 增加了2个方法
#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());MOCK_METHOD1(setValue, void(std::string& value));MOCK_METHOD2(setDoubleValues, void(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的形参可以传入任意参数 - 在第24~25行: 让
setDoubleValues第二个参数得大于等于1,但我实际传入时却传入一个0。这时程序运行时就报错了: ```shell unknown file: error: Unexpected mock function call - returning directly. Function call: setDoubleValues(1, 0) Google Mock tried the following 1 expectation, but it didn’t match:
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(27): 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 C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(27): error: 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
<a name="nIPq5"></a>## 成员匹配器| (&class::field, m) | argument.field (或 argument->field, 当argument是一个指针时)与匹配器m匹配, 这里的argument是一个class类的实例. || --- | --- || Key(e) | 形参(argument)比较是一个类似map这样的容器,然后argument.first的值等于e || Pair(m1, m2) | 形参(argument)必须是一个pair,并且argument.first等于m1,argument.second等于m2. || Property(&class::property, m) | argument.property()(或argument->property(),当argument是一个指针时)与匹配器m匹配, 这里的argument是一个class类的实例. |```cppTEST(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();}
- 4行,定义了一个
Field(&Bar::num, Ge(0)),以说明Bar的成员变量num必须大于等于0。
上面这个是正确的例子,我们为了说明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);
}
未找到
Bar的源码
#pragma once
class Bar
{
public:
virtual ~Bar() {}
public:
int num = 0;
};
#ifndef MOCKFOO_H_
#define MOCKFOO_H_
#include <gmock/gmock.h>
#include <string>
#include "Bar.h"
namespace seamless {
class MockFoo1 : public Bar {
public:
MOCK_METHOD1(get, void(Bar& bar));
};
} // namespace seamless
#endif // MOCKFOO_H_
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
using ::testing::Return;
using ::testing::Ge;
using ::testing::Field;
TEST(TestField, Simple) {
MockFoo1 mockFoo1;
Bar bar;
// bar.num = -1;
EXPECT_CALL(mockFoo1, get(Field(&Bar::num, Ge(0)))).Times(1);
mockFoo1.get(bar);
}
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TestField
[ RUN ] TestField.Simple
[ OK ] TestField.Simple (1 ms)
[----------] 1 test from TestField (2 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (4 ms total)
[ PASSED ] 1 test.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
using ::testing::Return;
using ::testing::Ge;
using ::testing::Field;
TEST(TestField, Simple) {
MockFoo1 mockFoo1;
Bar bar;
bar.num = -1;
EXPECT_CALL(mockFoo1, get(Field(&Bar::num, Ge(0)))).Times(1);
mockFoo1.get(bar);
}
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TestField
[ RUN ] TestField.Simple
unknown file: error:
Unexpected mock function call - returning directly.
Function call: get(@00000014376FEFA8 16-byte object <20-63 E2-73 F6-7F 00-00 FF-FF FF-FF CC-CC CC-CC>)
Google Mock tried the following 1 expectation, but it didn't match:
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(19): EXPECT_CALL(mockFoo1, get(Field(&Bar::num, Ge(0))))...
Expected arg #0: is an object whose given field is >= 0
Actual: 16-byte object <20-63 E2-73 F6-7F 00-00 FF-FF FF-FF CC-CC CC-CC>, whose given field is -1 (of type int)
Expected: to be called once
Actual: never called - unsatisfied and active
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(19): error: Actual function call count doesn't match EXPECT_CALL(mockFoo1, get(Field(&Bar::num, Ge(0))))...
Expected: to be called once
Actual: never called - unsatisfied and active
[ FAILED ] TestField.Simple (2 ms)
[----------] 1 test from TestField (3 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (4 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] TestField.Simple
1 FAILED TEST
匹配函数或函数对象的返回值
| ResultOf(f, m) | f(argument) 与匹配器m匹配, 这里的f是一个函数或函数对象. |
|---|---|
指针匹配器
| Pointee(m) | argument (不论是智能指针还是原始指针) 指向的值与匹配器m匹配. |
|---|---|
复合匹配器
| AllOf(m1, m2, …, mn) | argument 匹配所有的匹配器m1到mn |
|---|---|
| AnyOf(m1, m2, …, mn) | argument 至少匹配m1到mn中的一个 |
| Not(m) | argument 不与匹配器m匹配 |
传入的参数必须 >5 并且 <= 10
EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));第一个参数不包含
“blah”这个子串EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
基数(Cardinalities)
基数用于Times()中来指定模拟函数将被调用多少次|
| AnyNumber() | 函数可以被调用任意次. |
|---|---|
| AtLeast(n) | 预计至少调用n次. |
| AtMost(n) | 预计至多调用n次. |
| Between(m, n) | 预计调用次数在m和n(包括n)之间. |
| Exactly(n) 或 n | 预计精确调用n次. 特别是, 当n为0时,函数应该永远不被调用. |
行为(Actions)
Actions(行为)用于指定Mock类的方法所期望模拟的行为
- 比如返回什么样的值、对引用、指针赋上怎么样个值,等等。
值的返回
| Return() | 让Mock方法返回一个void结果 |
|---|---|
| Return(value) | 返回值value |
| ReturnNull() | 返回一个NULL指针 |
| ReturnRef(variable) | 返回variable的引用. |
| ReturnPointee(ptr) | 返回一个指向ptr的指针 |
另一面的作用(Side Effects)
| Assign(&variable, value) | 将value分配给variable |
|---|---|
使用函数或者函数对象(Functor)作为行为
| Invoke(f) | 使用模拟函数的参数调用f, 这里的f可以是全局/静态函数或函数对象. |
|---|---|
| Invoke(object_pointer, &class::method) | 使用模拟函数的参数调用object_pointer对象的mothod方法. |
复合动作
| DoAll(a1, a2, …, an) | 每次发动时执行a1到an的所有动作. |
|---|---|
| IgnoreResult(a) | 执行动作a并忽略它的返回值. a不能返回void. |
这里我举个例子来解释一下DoAll()的作用,我个人认为这个DoAll()还是挺实用的。
例如有一个Mock方法:
virtual int getParamter(std::string* name, std::string* value) = 0
对于这个方法,我这回需要操作的结果是将name指向value的地址,并且得到方法的返回值。
类似这样的需求,我们就可以这样定义期望过程:
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);
}
这时就用上了我们的DoAll()了,它将Assign()和Return()结合起来了。
序列(Sequences)
默认时,对于定义要的期望行为是无序(Unordered)的,即当我定义好了如下的期望行为
MockFoo mockFoo;
EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
对于这样的期望行为的定义,我何时调用mockFoo.getValue()或者何时mockFoo.getSize()都可以的。
但有时候我们需要定义有序的(Ordered)的调用方式,即序列 (Sequences) 指定预期的顺序.
在同一序列里的所有预期调用必须按它们指定的顺序发生; 反之则可以是任意顺序.
#ifndef MOCKFOO_H_
#define MOCKFOO_H_
#include <gmock/gmock.h>
#include <string>
#include "FooInterface.h"
#include "Bar.h"
namespace seamless {
class MockFoo : public FooInterface {
public:
MOCK_METHOD0(getArbitraryString, std::string());
MOCK_METHOD0(getSize, int());
MOCK_METHOD0(getValue, std::string());
MOCK_METHOD1(setValue, void(std::string& value));
MOCK_METHOD2(setDoubleValues, void(int x, int y));
};
class MockFoo1 : public Bar {
public:
MOCK_METHOD1(get, void(Bar &bar));
};
} // namespace seamless
#endif // MOCKFOO_H_
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
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;
}
- 第17行建立两个序列
- 第19行,
EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2).WillOnce(Return(1)): 说明getSize()的行为优先于s1、s2. - 第20行,
EXPECT_CALL(mockFoo, getValue()).InSequence(s1): 说明getValue()的行为在序列s1中
当我尝试一下把First: 1 Second: Hello World!mockFoo.getSize()和mockFoo.getValue()的调用对调时试试: ```cppinclude
include
include
include
include “MockFoo.h”
using namespace seamless; using namespace std;
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 << "Second:\t" << mockFoo.getValue() << endl;
cout << "First:\t" << mockFoo.getSize() << endl;
return EXIT_SUCCESS;
}
```shell
unknown file: error:
Unexpected mock function call - returning default value.
Function call: getValue()
Returns: ""
Google Mock tried the following 1 expectation, but it didn't match:
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(20): EXPECT_CALL(mockFoo, getValue())...
Expected: all pre-requisites are satisfied
Actual: the following immediate pre-requisites are not satisfied:
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(19): pre-requisite #0
(end of pre-requisites)
Expected: to be called once
Actual: never called - unsatisfied and active
Second:
First: 1
C:\DevelopProject\GitLab\agent_googletest\gmock _01\FooMain.cpp(20): error: 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)的顺序而自动地识别调用顺序,这种方式可能更为地通用
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
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;
}
Mock实践
这个例子的背景是用于搜索引擎的:
- 引擎接收一个查询的Query,比如http://127.0.0.1/search?q=mp3&retailwholesale=0&isuse_alipay=1
- 引擎接收到这个Query后,将解析这个Query,将Query的Segment(如q=mp3、retail_wholesale=0放到一个数据结构中)
- 引擎会调用另外内部模块具体根据这些Segment来处理相应的业务逻辑。
由于Google Mock不能Mock模版方法,因此我稍微更改了一下原本的接口,以便演示:
改过的例子
先来看看引擎定义好的接口们:
VariantField.h 一个联合体,用于保存Query中的Segment的值
#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_
IParameterInterface.h 提供一个接口,用于得到Query中的各个Segment的值
#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_
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_
引擎定义好的接口就以上三个,下面是引擎中的一个模块用于根据Query中的Segment接合业务处理的。
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_
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
- 从上面的例子中可以看出,引擎会传入一个
IAPIProviderInterface对象,这个对象调用getParameterInterface()方法来得到Query中的Segment。 - 因此,我们需要Mock的对象也比较清楚了,就是要模拟引擎将
Query的Segment传给这个模块。其实就是让=模拟iParameter->getParameter方法:我想让它返回什么样的值就返回什么样的值.
下面我们开始Mock了:MockIParameterInterface.h 模拟模拟IParameterInterface类
#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_METHOD2(getParameter, int32_t(const char* name, VariantField*& value));
};
} // namespace seamless
#endif // MOCKIPARAMETERINTERFACE_H_
MockIAPIProviderInterface.h 模拟IAPIProviderInterface类
#ifndef MOCKIAPIPROVIDERINTERFACE_H_
#define MOCKIAPIPROVIDERINTERFACE_H_
#include <gmock/gmock.h>
#include "IAPIProviderInterface.h"
#include "IParameterInterface.h"
namespace seamless {
class MockIAPIProviderInterface: public IAPIProviderInterface{
public:
MOCK_METHOD0(getParameterInterface, IParameterInterface*());
};
} // namespace seamless
#endif // MOCKIAPIPROVIDERINTERFACE_H_
tester.cc 一个测试程序,试试我们的Mock成果
boost预编译版本:
#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;
EXPECT_CALL(*iAPIProvider, getParameterInterface()).Times(AtLeast(1)).
WillRepeatedly(Return(iParameter));
boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
retailWholesaleValue->strVal = "0";
boost::shared_ptr<VariantField> defaultValue(new VariantField);
defaultValue->strVal = "9";
EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).
WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).
WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
Rank rank;
rank.processQuery(iAPIProvider);
delete iAPIProvider;
return EXIT_SUCCESS;
}
