Google Mock 入門概述什麼是Mock?正則表達式
Mock,更確切地說應該是Mock Object。它到底是什麼?它有什麼做用?在這裏,我也只能先說說個人理解。 好比當咱們在單元測試、模塊的接口測試時,當這個模塊須要依賴另一個/幾個類,而這時這些個類尚未開發好(那名開發同窗比較懶,呵呵),這時咱們就能夠定義了Mock對象來模擬那些類的行爲。
說得更直白一些,就是本身實現一個假的依賴類,對這個類的方法你想要什麼行爲就能夠有什麼行爲,你想讓這個方法返回什麼結果就能夠返回怎麼樣的結果。
但這時不少同窗每每會提出一個問題:"那既然是我本身實現一個假的依賴類",那和那些市面上的Mock框架有什麼關係啊?
這個實際上是這樣的,這些個Mock框架能夠幫助你比較方便、比較輕鬆地實現這些個假的依賴類。畢竟,若是你實現這麼一個假的依賴類的時間花費過場的話,那我還不如等待那位懶惰的同窗吧。api
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
-
- 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.hless
- #ifndef MOCKFOO_H_
- #define MOCKFOO_H_
-
- #include
- #include
- #include "FooInterface.h"
-
- namespace seamless {
-
- class MockFoo: public FooInterface {
- public:
- MOCK_METHOD0(getArbitraryString, std::string());
- };
-
- } // namespace seamless
-
- #endif // MOCKFOO_H_
咱們稍微來解釋一下這個Mock類的定義:
- 第10行咱們的MockFoo類繼承懶同窗的FooInterface
- 第22行咱們定義使用gmock中的一個宏(Macro)MOCK_METHOD0來定義MockFoo中的getArbitraryString。Google Mock是須要你根據不一樣的形參個數來使用不一樣的Mock Method,我這裏getArbitraryString沒有函數,就是MOCK_METHOD0了,同理,若是是一個形參,就是MOCK_METHOD1了,以此往下。
FooMain.ccide
- #include
- #include
- #include
- #include
- #include
-
- #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;
- }
最後咱們運行編譯,獲得的結果以下:函數
Returned Value: Hello World!
在這裏:
- 第15行,初始化一個Google Mock
- 第18行,聲明一個MockFoo的對象:mockFoo
- 第19行,是爲MockFoo的getArbitraryString()方法定義一個指望行爲,其中Times(1)的意思是運行一次,WillOnce(Return(value))的意思是第一次運行時把value做爲getArbitraryString()方法的返回值。
這就是咱們最簡單的使用Google Mock的例子了,使用起來的確比較簡便吧。post
典型的流程
經過上述的例子,已經能夠看出使用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() 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
- #include
- #include
- #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_
FooMain.h
- #include
- #include
- #include
- #include
-
- #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
上述的那些匹配器都比較簡單,下面咱們來看看那些比較複雜的匹配吧。
成員匹配器
Field(&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類的實例. |
仍是舉例說明一下:
- 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();
- }
這裏咱們使用
Google Test來寫個測試用例,這樣看得比較清楚。
- 第5行,咱們定義了一個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);
- }
運行是出錯了:
[==========] 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
匹配函數或函數對象的返回值
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匹配 |
- EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));
- 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) 指定預期的順序. 在同一序列裏的全部預期調用必須按它們指定的順序發生; 反之則能夠是任意順序.
- 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行創建兩個序列:s一、s2。
- 而後在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)說明getSize()的行爲優先於s一、s2.
- 而第12行時,EXPECT_CALL(mockFoo, getValue()).InSequence(s1)說明getValue()的行爲在序列s1中。
獲得的結果以下:
First: 1
Second: Hello World!
當我嘗試一下把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;
- }
Mock實踐
下面我從我在工做中參與的項目中選取了一個實際的例子來實踐Mock。
這個例子的背景是用於搜索引擎的:
- 引擎接收一個查詢的Query,好比http://127.0.0.1/search?q=mp3&retailwholesale=0&isuse_alipay=1
- 引擎接收到這個Query後,將解析這個Query,將Query的Segment(如q=mp三、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
- #include
- #include
- #include
- #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
-
- #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
-
- #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成果
- #include <boost/cstdint.hpp>
- #include <boost/shared_ptr.hpp>
- #include
- #include
-
- #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;
- }
- 第26行,定義一個執行順序,所以在以前的Rank.cc中,是先調用iAPIProvider>getParameterInterface,而後再調用iParameter>getParameter,所以咱們在下面會先定義MockIAPIProviderInterface.getParameterInterface的指望行爲,而後再是其餘的。
- 第27~28行,定義MockIAPIProviderInterface.getParameterInterface的的行爲:程序至少被調用一次(Times(AtLeast(1))),每次調用都返回一個iParameter(即MockIParameterInterface*的對象)。
- 第30~34行,我本身假設了一些Query的Segment的值。即我想達到的效果是Query相似http://127.0.0.1/search?retailwholesale=0&isuse_alipay=9。
- 第36~38行,咱們定義MockIParameterInterface.getParameter的指望行爲:這個方法至少被調用一次;第一次被調用時返回1並將第一個形參指向retailWholesaleValue;後續幾回被調用時返回1,並指向defaultValue。
- 第51行,運行Rank類下的processQuery方法。
看看咱們的運行成果:
isRetailWholesale: 0
isUseAlipay: 1
從這個結果驗證出咱們傳入的Query信息是對的,成功Mock!
現實中的例子
就如我以前所說的,上述的那個例子是我改過的,現實項目中哪有這麼理想的結構(特別對於那些歷來沒有Develop for Debug思想的同窗)。
所以咱們來看看上述這個例子中實際的代碼:其實只有IAPIProviderInterface.h不一樣,它定義了一個模版函數,用於統一各類類型的接口: IAPIProviderInterface.h 真正的IAPIProviderInterface.h,有一個模版函數
- #ifndef IAPIPROVIDERINTERFACE_H_
- #define IAPIPROVIDERINTERFACE_H_
-
- #include <boost/cstdint.hpp>
- #include
-
- #include "IBaseInterface.h"
- #include "IParameterInterface.h"
- #include "VariantField.h"
-
- namespace seamless {
-
- class IAPIProviderInterface: public IBaseInterface {
- public:
- IAPIProviderInterface() {}
- virtual ~IAPIProviderInterface() {}
-
- public:
- virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;
-
- template<typename InterfaceType>
- InterfaceType* getInterface() {
- IBaseInterface* pInterface = NULL;
- if (queryInterface(pInterface)) {
- std::cerr << "Query Interface failed" << std::endl;
- }
- return static_cast<InterfaceType* >(pInterface);
- }
- };
-
- }
-
- #endif // IAPIPROVIDERINTERFACE_H_
Rank.cc 既然IAPIProviderInterface.h改了,那Rank.cc中對它的調用其實也不是以前那樣的。不過其實也就差一行代碼:
- // IParameterInterface* iParameter = iAPIProvider->getParameterInterface();
- IParameterInterface* iParameter = iAPIProvider->getInterface<IParameterInterface>();
由於目前版本(1.5版本)的Google Mock還不支持模版函數,所以咱們沒法Mock IAPIProviderInterface中的getInterface,那咱們如今怎麼辦?
若是你想作得比較完美的話我暫時也沒想出辦法,我如今可以想出的辦法也只能這樣:IAPIProviderInterface.h 修改其中的getInterface,讓它根據模版類型,若是是IParameterInterface或者MockIParameterInterface則就返回一個MockIParameterInterface的對象
- #ifndef IAPIPROVIDERINTERFACE_H_
- #define IAPIPROVIDERINTERFACE_H_
-
- #include <boost/cstdint.hpp>
- #include
-
- #include "IBaseInterface.h"
- #include "IParameterInterface.h"
- #include "VariantField.h"
-
- // In order to Mock
- #include <boost/shared_ptr.hpp>
- #include
- #include "MockIParameterInterface.h"
-
- namespace seamless {
-
- class IAPIProviderInterface: public IBaseInterface {
- public:
- IAPIProviderInterface() {}
- virtual ~IAPIProviderInterface() {}
-
- public:
- virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;
-
- template<typename InterfaceType>
- InterfaceType* getInterface() {
- IBaseInterface* pInterface = NULL;
- if (queryInterface(pInterface) == 0) {
- std::cerr << "Query Interface failed" << std::endl;
- }
-
- // In order to Mock
- if ((typeid(InterfaceType) == typeid(IParameterInterface)) ||
- (typeid(InterfaceType) == typeid(MockIParameterInterface))) {
- using namespace ::testing;
- MockIParameterInterface* iParameter = new MockIParameterInterface;
- 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)));
- return static_cast<InterfaceType* >(iParameter);
- }
- // end of mock
-
- return static_cast<InterfaceType* >(pInterface);
- }
- };
-
- }
-
- #endif // IAPIPROVIDERINTERFACE_H_
- 第33~49行,判斷傳入的模版函數的類型,而後定義相應的行爲,最後返回一個MockIParameterInterface對象
tester.cc
- int main(int argc, char** argv) {
- ::testing::InitGoogleMock(&argc, argv);
-
- MockIAPIProviderInterface* iAPIProvider = new MockIAPIProviderInterface;
-
- InSequence dummy;
- EXPECT_CALL(*iAPIProvider, queryInterface(_)).Times(AtLeast(1)).
- WillRepeatedly(Return(1));
-
- Rank rank;
- rank.processQuery(iAPIProvider);
-
- delete iAPIProvider;
-
- return EXIT_SUCCESS;
- }
- 這裏的調用就相對簡單了,只要一個MockIAPIProviderInterface就能夠了。
Google Mock Cookbook
這裏根據Google Mock Cookbook和我本身試用的一些經驗,整理一些試用方面的技巧。
Mock protected、private方法
Google Mock也能夠模擬protected和private方法,比較神奇啊(其實從這點上也能夠看出,Mock類不是簡單地繼承本來的接口,而後本身把它提供的方法實現;Mock類其實就等於本來的接口)。
對protected和private方法的Mock和public基本相似,只不過在Mock類中須要將這些方法設置成public。
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 模版類(Template Class)
Google Mock能夠Mock模版類,只要在宏MOCK*的後面加上T。
仍是相似上述那個例子:
Foo.h 改爲模版類
- template <typename T>
- class Foo {
- public:
- virtual void setValue(int value) {};
-
- public:
- int value;
- };
MockFoo.h
- template <typename T>
- class Foo {
- public:
- virtual void setValue(int value) {};
-
- public:
- int value;
- };
Nice Mocks 和 Strict Mocks
當在調用Mock類的方法時,若是以前沒有使用EXPECT_CALL來定義該方法的指望行爲時,Google Mock在運行時會給你一些警告信息:
GMOCK WARNING:
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0
Stack trace
對於這種狀況,可使用NiceMock來避免:
- // MockFoo mockFoo;
- NiceMock<MockFoo> mockFoo;
使用NiceMock來替代以前的MockFoo。
固然,另外還有一種辦法,就是使用StrictMock來將這些調用都標爲失敗:
- StrictMock<MockFoo> mockFoo;
這時獲得的結果:
unknown file: Failure
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0