google mock分享
|
Google Mock 入門
概述
什么是Mock?
Mock,更確切地說應(yīng)該是Mock Object。它究竟是什么?它有什么作用?在這里,我也只能先說說我的理解。 比如當我們在單元測試、模塊的接口測試時,當這個模塊需要依賴另外一個/幾個類,而這時這些個類還沒有開發(fā)好(那名開發(fā)同學(xué)比較懶,呵呵),這時我們就可以定義了Mock對象來模擬那些類的行為。
說得更直白一些,就是自己實現(xiàn)一個假的依賴類,對這個類的方法你想要什么行為就可以有什么行為,你想讓這個方法返回什么結(jié)果就可以返回怎么樣的結(jié)果。
但這時很多同學(xué)往往會提出一個問題:"那既然是我自己實現(xiàn)一個假的依賴類",那和那些市面上的Mock框架有什么關(guān)系啊?
這個其實是這樣的,這些個Mock框架可以幫助你比較方便、比較輕松地實現(xiàn)這些個假的依賴類。畢竟,如果你實現(xiàn)這么一個假的依賴類的時間花費過場的話,那我還不如等待那位懶惰的同學(xué)吧。
Google Mock概述
Google Mock(簡稱gmock)是Google在2008年推出的一套針對C++的Mock框架,它靈感取自于jMock、EasyMock、harcreat。它提供了以下這些特性:
- 輕松地創(chuàng)建mock類
- 支持豐富的匹配器(Matcher)和行為(Action)
- 支持有序、無序、部分有序的期望行為的定義
- 多平臺的支持
參考文檔
- 新人手冊
- Cheat Sheet
- Cheat Sheet中文翻譯
- Cookbook
Google Mock使用
最簡單的例子
我比較喜歡舉例來說明這些個、那些個玩意,因此我們先來看看Google Mock就簡單的用法和作用。
- 首先,那個懶惰的同學(xué)已經(jīng)定義好了這么一個接口(萬幸,他至少把接口定義好了):
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的析構(gòu)函數(shù)~FooInterface()必須是virtual的
- 在第13行,我們得把getArbitraryString定義為純虛函數(shù)。其實getArbitraryString()也不一定得是純虛函數(shù),這點我們后面會提到.
現(xiàn)在我們用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_我們稍微來解釋一下這個Mock類的定義:
- 第10行我們的MockFoo類繼承懶同學(xué)的FooInterface
- 第22行我們定義使用gmock中的一個宏(Macro)MOCK_METHOD0來定義MockFoo中的getArbitraryString。Google Mock是需要你根據(jù)不同的形參個數(shù)來使用不同的Mock Method,我這里getArbitraryString沒有函數(shù),就是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; }
最后我們運行編譯,得到的結(jié)果如下:
Returned Value: Hello World!在這里:
- 第15行,初始化一個Google Mock
- 第18行,聲明一個MockFoo的對象:mockFoo
- 第19行,是為MockFoo的getArbitraryString()方法定義一個期望行為,其中Times(1)的意思是運行一次,WillOnce(Return(value))的意思是第一次運行時把value作為getArbitraryString()方法的返回值。
這就是我們最簡單的使用Google Mock的例子了,使用起來的確比較簡便吧。
典型的流程
通過上述的例子,已經(jīng)可以看出使用Mock類的一般流程如下:
- 引入你要用到的Google Mock名稱. 除宏或其它特別提到的之外所有Google Mock名稱都位于*testing*命名空間之下.
- 建立模擬對象(Mock Objects).
- 可選的,設(shè)置模擬對象的默認動作.
- 在模擬對象上設(shè)置你的預(yù)期(它們怎樣被調(diào)用,應(yīng)該怎樣回應(yīng)?).
自定義方法/成員函數(shù)的期望行為
從上述的例子中可以看出,當我們針對懶同學(xué)的接口定義好了Mock類后,在單元測試/主程序中使用這個Mock類中的方法時最關(guān)鍵的就是對期望行為的定義。
對方法期望行為的定義的語法格式如下:
EXPECT_CALL(mock_object, method(matcher1, matcher2, ...)).With(multi_argument_matcher).Times(cardinality).InSequence(sequences).After(expectations).WillOnce(action).WillRepeatedly(action).RetiresOnSaturation();
解釋一下這些參數(shù)(雖然很多我也沒弄明白):
- 第1行的mock_object就是你的Mock類的對象
- 第1行的method(matcher1, matcher2, …)中的method就是你Mock類中的某個方法名,比如上述的getArbitraryString;而matcher(匹配器)的意思是定義方法參數(shù)的類型,我們待會詳細介紹。
- 第3行的Times(cardinality)的意思是之前定義的method運行幾次。至于cardinality的定義,我也會在后面詳細介紹。
- 第4行的InSequence(sequences)的意思是定義這個方法被執(zhí)行順序(優(yōu)先級),我會再后面舉例說明。
- 第6行WillOnce(action)是定義一次調(diào)用時所產(chǎn)生的行為,比如定義該方法返回怎么樣的值等等。
- 第7行WillRepeatedly(action)的意思是缺省/重復(fù)行為。
我稍微先舉個例子來說明一下,后面有針對更為詳細的說明:
這個期望行為的定義的意思是:
- 調(diào)用mockTurtle的getX()方法
- 這個方法會至少調(diào)用5次
- 第一次被調(diào)用時返回100
- 第2次被調(diào)用時返回150
- 從第3次被調(diào)用開始每次都返回200
Matcher(匹配器)
Matcher用于定義Mock類中的方法的形參的值(當然,如果你的方法不需要形參時,可以保持match為空。),它有以下幾種類型:(更詳細的介紹可以參見Google Mock Wiki上的Matcher介紹)
通配符
| _ | 可以代表任意類型 |
| A() or An() | 可以是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(value) | 形參的類型必須是type類型,而且值必須是value |
浮點數(shù)的比較
| DoubleEq(a_double) | 形參是一個double類型,比如值近似于a_double,兩個NaN是不相等的 |
| FloatEq(a_float) | 同上,只不過類型是float |
| NanSensitiveDoubleEq(a_double) | 形參是一個double類型,比如值近似于a_double,兩個NaN是相等的,這個是用戶所希望的方式 |
| NanSensitiveFloatEq(a_float) | 同上,只不過形參是float |
字符串匹配
這里的字符串即可以是C風(fēng)格的字符串,也可以是C++風(fēng)格的。
| ContainsRegex(string) | 形參匹配給定的正則表達式 |
| EndsWith(suffix) | 形參以suffix截尾 |
| HasSubstr(string) | 形參有string這個子串 |
| MatchesRegex(string) | 從第一個字符到最后一個字符都完全匹配給定的正則表達式. |
| StartsWith(prefix) | 形參以prefix開始 |
| StrCaseEq(string) | 參數(shù)等于string,并且忽略大小寫 |
| StrCaseNe(string) | 參數(shù)不是string,并且忽略大小寫 |
| StrEq(string) | 參數(shù)等于string |
| StrNe(string) | 參數(shù)不等于string |
容器的匹配
很多STL的容器的比較都支持==這樣的操作,對于這樣的容器可以使用上述的Eq(container)來比較。但如果你想寫得更為靈活,可以使用下面的這些容器匹配方法:
| Contains(e) | 在method的形參中,只要有其中一個元素等于e |
| Each(e) | 參數(shù)各個元素都等于e |
| ElementsAre(e0, e1, …, en) | 形參有n+1的元素,并且挨個匹配 |
| ElementsAreArray(array) 或者ElementsAreArray(array, count) | 和ElementsAre()類似,除了預(yù)期值/匹配器來源于一個C風(fēng)格數(shù)組 |
| ContainerEq(container) | 類型Eq(container),就是輸出結(jié)果有點不一樣,這里輸出結(jié)果會帶上哪些個元素不被包含在另一個容器中 |
| Pointwise(m, container) | ? |
上述的一些匹配器都比較簡單,我就隨便打包舉幾最簡單的例子演示一下吧: 我稍微修改一下之前的Foo.h和MockFoo.h,?MockFoo.h?增加了2個方法
-
#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_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
#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的形參可以傳入任意參數(shù)
- 另外,我在第26~27行故意犯了個錯(為了說明上述這些匹配器的作用),我之前明明讓setDoubleValues第二個參數(shù)得大于等于1,但我實際傳入時卻傳入一個0。這時程序運行時就報錯了:
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上述的那些匹配器都比較簡單,下面我們來看看那些比較復(fù)雜的匹配吧。
成員匹配器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。
上面這個是正確的例子,我們?yōu)榱苏f明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: FailureUnexpected 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.Simple1 FAILED TEST
匹配函數(shù)或函數(shù)對象的返回值
ResultOf(f, m) f(argument) 與匹配器m匹配, 這里的f是一個函數(shù)或函數(shù)對象. 指針匹配器
Pointee(m) argument (不論是智能指針還是原始指針) 指向的值與匹配器m匹配. 復(fù)合匹配器
EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));AllOf(m1, m2, …, mn) argument 匹配所有的匹配器m1到mn AnyOf(m1, m2, …, mn) argument 至少匹配m1到mn中的一個 Not(m) argument 不與匹配器m匹配
- 傳入的參數(shù)必須 >5 并且 <= 1EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
- 第一個參數(shù)不包含“blah”這個子串
基數(shù)(Cardinalities)
基數(shù)用于Times()中來指定模擬函數(shù)將被調(diào)用多少次|
AnyNumber() 函數(shù)可以被調(diào)用任意次. AtLeast(n) 預(yù)計至少調(diào)用n次. AtMost(n) 預(yù)計至多調(diào)用n次. Between(m, n) 預(yù)計調(diào)用次數(shù)在m和n(包括n)之間. Exactly(n) 或 n 預(yù)計精確調(diào)用n次. 特別是, 當n為0時,函數(shù)應(yīng)該永遠不被調(diào)用. 行為(Actions)
Actions(行為)用于指定Mock類的方法所期望模擬的行為:比如返回什么樣的值、對引用、指針賦上怎么樣個值,等等。?值的返回
Return() 讓Mock方法返回一個void結(jié)果 Return(value) 返回值value ReturnNull() 返回一個NULL指針 ReturnRef(variable) 返回variable的引用. ReturnPointee(ptr) 返回一個指向ptr的指針 另一面的作用(Side Effects)
Assign(&variable, value) 將value分配給variable 使用函數(shù)或者函數(shù)對象(Functor)作為行為
Invoke(f) 使用模擬函數(shù)的參數(shù)調(diào)用f, 這里的f可以是全局/靜態(tài)函數(shù)或函數(shù)對象. Invoke(object_pointer, &class::method) 使用模擬函數(shù)的參數(shù)調(diào)用object_pointer對象的mothod方法. 復(fù)合動作
DoAll(a1, a2, …, an) 每次發(fā)動時執(zhí)行a1到an的所有動作. IgnoreResult(a) 執(zhí)行動作a并忽略它的返回值. a不能返回void. 這里我舉個例子來解釋一下DoAll()的作用,我個人認為這個DoAll()還是挺實用的。例如有一個Mock方法:
virtual int getParamter(std::string* name, std::string* value) = 0
對于這個方法,我這回需要操作的結(jié)果是將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()結(jié)合起來了。
序列(Sequences)
默認時,對于定義要的期望行為是無序(Unordered)的,即當我定義好了如下的期望行為:
- MockFoo mockFoo;EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World"))); 對于這樣的期望行為的定義,我何時調(diào)用mockFoo.getValue()或者何時mockFoo.getSize()都可以的。
- 首先在第8行建立兩個序列:s1、s2。
- 然后在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)說明getSize()的行為優(yōu)先于s1、s2.
- 而第12行時,EXPECT_CALL(mockFoo, getValue()).InSequence(s1)說明getValue()的行為在序列s1中。
- 引擎接收一個查詢的Query,比如http://127.0.0.1/search?q=mp3&retailwholesale=0&isuse_alipay=1
- 引擎接收到這個Query后,將解析這個Query,將Query的Segment(如q=mp3、retail_wholesale=0放到一個數(shù)據(jù)結(jié)構(gòu)中)
- 引擎會調(diào)用另外內(nèi)部模塊具體根據(jù)這些Segment來處理相應(yīng)的業(yè)務(wù)邏輯。
- 從上面的例子中可以看出,引擎會傳入一個IAPIProviderInterface對象,這個對象調(diào)用getParameterInterface()方法來得到Query中的Segment。
- 因此,我們需要Mock的對象也比較清楚了,就是要模擬引擎將Query的Segment傳給這個模塊。其實就是讓=模擬iParameter->getParameter方法:我想讓它返回什么樣的值就返回什么樣的值.
- 第26行,定義一個執(zhí)行順序,因此在之前的Rank.cc中,是先調(diào)用iAPIProvider>getParameterInterface,然后再調(diào)用iParameter>getParameter,因此我們在下面會先定義MockIAPIProviderInterface.getParameterInterface的期望行為,然后再是其他的。
- 第27~28行,定義MockIAPIProviderInterface.getParameterInterface的的行為:程序至少被調(diào)用一次(Times(AtLeast(1))),每次調(diào)用都返回一個iParameter(即MockIParameterInterface*的對象)。
- 第30~34行,我自己假設(shè)了一些Query的Segment的值。即我想達到的效果是Query類似http://127.0.0.1/search?retailwholesale=0&isuse_alipay=9。
- 第36~38行,我們定義MockIParameterInterface.getParameter的期望行為:這個方法至少被調(diào)用一次;第一次被調(diào)用時返回1并將第一個形參指向retailWholesaleValue;后續(xù)幾次被調(diào)用時返回1,并指向defaultValue。
- 第51行,運行Rank類下的processQuery方法。
- 第33~49行,判斷傳入的模版函數(shù)的類型,然后定義相應(yīng)的行為,最后返回一個MockIParameterInterface對象
- 這里的調(diào)用就相對簡單了,只要一個MockIAPIProviderInterface就可以了。
-
-
但有時候我們需要定義有序的(Ordered)的調(diào)用方式,即序列 (Sequences)?指定預(yù)期的順序. 在同一序列里的所有預(yù)期調(diào)用必須按它們指定的順序發(fā)生; 反之則可以是任意順序.
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; }得到的結(jié)果如下:
First: 1Second: Hello World!
當我嘗試一下把mockFoo.getSize()和mockFoo.getValue()的調(diào)用對調(diào)時試試:
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的序列,而根據(jù)我定義期望行為(EXPECT_CALL)的順序而自動地識別調(diào)用順序,這種方式可能更為地通用。
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。
這個例子的背景是用于搜索引擎的:
由于Google Mock不能Mock模版方法,因此我稍微更改了一下原本的接口,以便演示:
我改過的例子
我們先來看看引擎定義好的接口們:
VariantField.h?一個聯(lián)合體,用于保存Query中的Segment的值
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?一個統(tǒng)一的外部接口
#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_引擎定義好的接口就以上三個,下面是引擎中的一個模塊用于根據(jù)Query中的Segment接合業(yè)務(wù)處理的。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?實現(xiàn)#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 下面我們開始Mock了:
MockIParameterInterface.h?模擬模擬IParameterInterface類
#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; }
看看我們的運行成果:
isRetailWholesale: 0isUseAlipay: 1
從這個結(jié)果驗證出我們傳入的Query信息是對的,成功Mock!
現(xiàn)實中的例子
就如我之前所說的,上述的那個例子是我改過的,現(xiàn)實項目中哪有這么理想的結(jié)構(gòu)(特別對于那些從來沒有Develop for Debug思想的同學(xué))。
因此我們來看看上述這個例子中實際的代碼:其實只有IAPIProviderInterface.h不同,它定義了一個模版函數(shù),用于統(tǒng)一各種類型的接口:?IAPIProviderInterface.h?真正的IAPIProviderInterface.h,有一個模版函數(shù)
如果你想做得比較完美的話我暫時也沒想出辦法,我現(xiàn)在能夠想出的辦法也只能這樣:IAPIProviderInterface.h?修改其中的getInterface,讓它根據(jù)模版類型,如果是IParameterInterface或者MockIParameterInterface則就返回一個MockIParameterInterface的對象#ifndef IAPIPROVIDERINTERFACE_H_ #define IAPIPROVIDERINTERFACE_H_#include <boost/cstdint.hpp> #include <iostream>#include "IBaseInterface.h" #include "IParameterInterface.h" #include "VariantField.h"// In order to Mock #include <boost/shared_ptr.hpp> #include <gmock/gmock.h> #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 Mockif ((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 mockreturn static_cast<InterfaceType* >(pInterface);} };}#endif // IAPIPROVIDERINTERFACE_H_
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; }Google Mock Cookbook
這里根據(jù)Google Mock Cookbook和我自己試用的一些經(jīng)驗,整理一些試用方面的技巧。
Mock protected、private方法
Google Mock也可以模擬protected和private方法,比較神奇啊(其實從這點上也可以看出,Mock類不是簡單地繼承原本的接口,然后自己把它提供的方法實現(xiàn);Mock類其實就等于原本的接口)。
對protected和private方法的Mock和public基本類似,只不過在Mock類中需要將這些方法設(shè)置成public。
Foo.h?帶private方法的接口
Mock 模版類(Template Class)
Google Mock可以Mock模版類,只要在宏MOCK*的后面加上T。
還是類似上述那個例子:
Foo.h?改成模版類
Nice Mocks 和 Strict Mocks
當在調(diào)用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來將這些調(diào)用都標為失敗:
StrictMock<MockFoo> mockFoo;這時得到的結(jié)果:
unknown file: Failure
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0 ?
總結(jié)
以上是生活随笔為你收集整理的google mock分享的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++:undefined refere
- 下一篇: json-c库使用总结