google mock C++單元測試框架

 http://blog.chinaunix.net/uid-25748718-id-3129590.htmlhtml

Google Mock 入門概述什麼是Mock?正則表達式

Mock,更確切地說應該是Mock Object。它到底是什麼?它有什麼做用?在這裏,我也只能先說說個人理解。 好比當咱們在單元測試、模塊的接口測試時,當這個模塊須要依賴另一個/幾個類,而這時這些個類尚未開發好(那名開發同窗比較懶,呵呵),這時咱們就能夠定義了Mock對象來模擬那些類的行爲。
說得更直白一些,就是本身實現一個假的依賴類,對這個類的方法你想要什麼行爲就能夠有什麼行爲,你想讓這個方法返回什麼結果就能夠返回怎麼樣的結果。
但這時不少同窗每每會提出一個問題:"那既然是我本身實現一個假的依賴類",那和那些市面上的Mock框架有什麼關係啊?
這個實際上是這樣的,這些個Mock框架能夠幫助你比較方便、比較輕鬆地實現這些個假的依賴類。畢竟,若是你實現這麼一個假的依賴類的時間花費過場的話,那我還不如等待那位懶惰的同窗吧。api

Google Mock概述

Google Mock(簡稱gmock)是Google在2008年推出的一套針對C++的Mock框架,它靈感取自於jMockEasyMockharcreat。它提供瞭如下這些特性:數組

  • 輕鬆地建立mock類
  • 支持豐富的匹配器(Matcher)和行爲(Action)
  • 支持有序、無序、部分有序的指望行爲的定義
  • 多平臺的支持
參考文檔 Google Mock使用最簡單的例子

我比較喜歡舉例來講明這些個、那些個玩意,所以咱們先來看看Google Mock就簡單的用法和做用。數據結構

  • 首先,那個懶惰的同窗已經定義好了這麼一個接口(萬幸,他至少把接口定義好了):

FooInterface.h框架

  1. #ifndef FOOINTERFACE_H_
  2. #define FOOINTERFACE_H_
  3.  
  4. #include 
  5.  
  6. namespace seamless {
  7.  
  8. class FooInterface {
  9. public:
  10.         virtual ~FooInterface() {}
  11.  
  12. public:
  13.         virtual std::string getArbitraryString() = 0;
  14. };
  15.  
  16. }  // namespace seamless
  17.  
  18. #endif // FOOINTERFACE_H_
這裏須要注意幾點:

 

  • FooInterface的析構函數~FooInterface()必須是virtual的
  • 在第13行,咱們得把getArbitraryString定義爲純虛函數。其實getArbitraryString()也不必定得是純虛函數,這點咱們後面會提到.

如今咱們用Google Mock來定義Mock類 FooMock.hless

  1. #ifndef MOCKFOO_H_
  2. #define MOCKFOO_H_
  3.  
  4. #include 
  5. #include 
  6. #include "FooInterface.h"
  7.  
  8. namespace seamless {
  9.  
  10. class MockFoo: public FooInterface {
  11. public:
  12.         MOCK_METHOD0(getArbitraryString, std::string());
  13. };
  14.  
  15. }  // namespace seamless
  16.  
  17. #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

  1. #include 
  2. #include 
  3. #include 
  4. #include 
  5. #include 
  6.  
  7. #include "MockFoo.h"
  8.  
  9. using namespace seamless;
  10. using namespace std;
  11.  
  12. using ::testing::Return;
  13.  
  14. int main(int argc, char** argv) {
  15.         ::testing::InitGoogleMock(&argc, argv);
  16.  
  17.         string value = "Hello World!";
  18.         MockFoo mockFoo;
  19.         EXPECT_CALL(mockFoo, getArbitraryString()).Times(1).
  20.                 WillOnce(Return(value));
  21.         string returnValue = mockFoo.getArbitraryString();
  22.         cout << "Returned Value: " << returnValue << endl;
  23.  
  24.         return EXIT_SUCCESS;
  25. }

最後咱們運行編譯,獲得的結果以下:函數

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類中的方法時最關鍵的就是對指望行爲的定義。
對方法指望行爲的定義的語法格式以下:

  1. EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
  2.     .With(multi_argument_matcher)
  3.     .Times(cardinality)
  4.     .InSequence(sequences)
  5.     .After(expectations)
  6.     .WillOnce(action)
  7.     .WillRepeatedly(action)
  8.     .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)的意思是缺省/重複行爲。

我稍微先舉個例子來講明一下,後面有針對更爲詳細的說明:

  1. EXPECT_CALL(mockTurtle, getX()).Times(testing::AtLeast(5)).
  2.                 WillOnce(testing::Return(100)).WillOnce(testing::Return(150)).
  3.                 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.hMockFoo.h, MockFoo.h 增長了2個方法

  1. #ifndef MOCKFOO_H_
  2. #define MOCKFOO_H_
  3.  
  4. #include 
  5. #include 
  6. #include 
  7. #include "FooInterface.h"
  8.  
  9. namespace seamless {
  10.  
  11. class MockFoo: public FooInterface {
  12. public:
  13.         MOCK_METHOD0(getArbitraryString, std::string());
  14.         MOCK_METHOD1(setValue, void(std::string& value));
  15.         MOCK_METHOD2(setDoubleValues, void(int x, int y));
  16. };
  17.  
  18. }  // namespace seamless
  19.  
  20. #endif // MOCKFOO_H_

FooMain.h

  1. #include 
  2. #include 
  3. #include 
  4. #include 
  5.  
  6. #include "MockFoo.h"
  7.  
  8. using namespace seamless;
  9. using namespace std;
  10.  
  11. using ::testing::Assign;
  12. using ::testing::Eq;
  13. using ::testing::Ge;
  14. using ::testing::Return;
  15.  
  16. int main(int argc, char** argv) {
  17.         ::testing::InitGoogleMock(&argc, argv);
  18.  
  19.         string value = "Hello World!";
  20.         MockFoo mockFoo;
  21.  
  22.         EXPECT_CALL(mockFoo, setValue(testing::_));
  23.         mockFoo.setValue(value);
  24.  
  25.         // 這裏我故意犯錯
  26.         EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)));
  27.         mockFoo.setDoubleValues(1, 0);
  28.  
  29.         return EXIT_SUCCESS;
  30. }
  • 第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類的實例.

仍是舉例說明一下:

  1. TEST(TestField, Simple) {
  2.         MockFoo mockFoo;
  3.         Bar bar;
  4.         EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
  5.         mockFoo.get(bar);
  6. }
  7.  
  8. int main(int argc, char** argv) {
  9.         ::testing::InitGoogleMock(&argc, argv);
  10.         return RUN_ALL_TESTS();
  11. }
這裏咱們使用 Google Test來寫個測試用例,這樣看得比較清楚。

 

  • 第5行,咱們定義了一個Field(&Bar::num, Ge(0)),以說明Bar的成員變量num必須大於等於0。

上面這個是正確的例子,咱們爲了說明Field的做用,傳入一個bar.num = -1試試。

  1. TEST(TestField, Simple) {
  2.         MockFoo mockFoo;
  3.         Bar bar;
  4.         bar.num = -1;
  5.         EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
  6.         mockFoo.get(bar);
  7. }
運行是出錯了:

 

[==========] 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匹配
  1. EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));
  • 傳入的參數必須 >5 而且 <= 10
  1. EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
  • 第一個參數不包含「blah」這個子串
基數(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方法:

  1. virtual int getParamter(std::string* name,  std::string* value) = 0

對於這個方法,我這回須要操做的結果是將name指向value的地址,而且獲得方法的返回值。
相似這樣的需求,咱們就能夠這樣定義指望過程:

  1. TEST(SimpleTest, F1) {
  2.     std::string* a = new std::string("yes");
  3.     std::string* b = new std::string("hello");
  4.     MockIParameter mockIParameter;
  5.     EXPECT_CALL(mockIParameter, getParamter(testing::_, testing::_)).Times(1).\
  6.         WillOnce(testing::DoAll(testing::Assign(&a, b), testing::Return(1)));
  7.     mockIParameter.getParamter(a, b);
  8. }
這時就用上了咱們的DoAll()了,它將Assign()和Return()結合起來了。
序列(Sequences)

默認時,對於定義要的指望行爲是無序(Unordered)的,即當我定義好了以下的指望行爲:

  1.         MockFoo mockFoo;
  2.         EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
  3.         EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
對於這樣的指望行爲的定義,我什麼時候調用 mockFoo.getValue()或者什麼時候 mockFoo.getSize()均可以的。

但有時候咱們須要定義有序的(Ordered)的調用方式,即序列 (Sequences) 指定預期的順序. 在同一序列裏的全部預期調用必須按它們指定的順序發生; 反之則能夠是任意順序.

  1. using ::testing::Return;
  2. using ::testing::Sequence;
  3.  
  4. int main(int argc, char **argv) {
  5.         ::testing::InitGoogleMock(&argc, argv);
  6.  
  7.         Sequence s1, s2;
  8.         MockFoo mockFoo;
  9.         EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2).WillOnce(Return(1));
  10.         EXPECT_CALL(mockFoo, getValue()).InSequence(s1).WillOnce(Return(
  11.                 string("Hello World!")));
  12.         cout << "First:\t" << mockFoo.getSize() << endl;
  13.         cout << "Second:\t" << mockFoo.getValue() << endl;
  14.  
  15.         return EXIT_SUCCESS;
  16. }
  • 首先在第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()的調用對調時試試:

  1.         cout << "Second:\t" << mockFoo.getValue() << endl;
  2.         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)的順序而自動地識別調用順序,這種方式可能更爲地通用。

  1. using ::testing::InSequence;
  2. using ::testing::Return;
  3.  
  4. int main(int argc, char **argv) {
  5.         ::testing::InitGoogleMock(&argc, argv);
  6.  
  7.         InSequence dummy;
  8.         MockFoo mockFoo;
  9.         EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
  10.         EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
  11.  
  12.         cout << "First:\t" << mockFoo.getSize() << endl;
  13.         cout << "Second:\t" << mockFoo.getValue() << endl;
  14.  
  15.         return EXIT_SUCCESS;
  16. }
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的值

  1. #ifndef VARIANTFIELD_H_
  2. #define VARIANTFIELD_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5.  
  6. namespace seamless {
  7.  
  8. union VariantField
  9. {
  10.     const char * strVal;
  11.     int32_t intVal;
  12. };
  13.  
  14. }  // namespace mlr_isearch_api
  15.  
  16. #endif // VARIANTFIELD_H_

IParameterInterface.h 提供一個接口,用於獲得Query中的各個Segment的值

  1. #ifndef IPARAMETERINTERFACE_H_
  2. #define IPARAMETERINTERFACE_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5.  
  6. #include "VariantField.h"
  7.  
  8. namespace seamless {
  9.  
  10. class IParameterInterface {
  11. public:
  12.         virtual ~IParameterInterface() {};
  13.  
  14. public:
  15.         virtual int32_t getParameter(const char* name,  VariantField*& value) = 0;
  16. };
  17.  
  18. }  // namespace
  19.  
  20. #endif // IPARAMETERINTERFACE_H_

IAPIProviderInterface.h 一個統一的外部接口

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5.  
  6. #include "IParameterInterface.h"
  7. #include "VariantField.h"
  8.  
  9. namespace seamless {
  10.  
  11. class IAPIProviderInterface {
  12. public:
  13.         IAPIProviderInterface() {}
  14.         virtual ~IAPIProviderInterface() {}
  15.  
  16. public:
  17.         virtual IParameterInterface* getParameterInterface() = 0;
  18. };
  19.  
  20. }
  21.  
  22. #endif // IAPIPROVIDERINTERFACE_H_

引擎定義好的接口就以上三個,下面是引擎中的一個模塊用於根據Query中的Segment接合業務處理的。Rank.h 頭文件

  1. #ifndef RANK_H_
  2. #define RANK_H_
  3.  
  4. #include "IAPIProviderInterface.h"
  5.  
  6. namespace seamless {
  7.  
  8. class Rank {
  9. public:
  10.         virtual ~Rank() {}
  11.  
  12. public:
  13.         void processQuery(IAPIProviderInterface* iAPIProvider);
  14. };
  15.  
  16. }  // namespace seamless
  17.  
  18. #endif // RANK_H_

Rank.cc 實現

  1. #include 
  2. #include 
  3. #include 
  4. #include 
  5. #include "IAPIProviderInterface.h"
  6. #include "IParameterInterface.h"
  7. #include "VariantField.h"
  8.  
  9. #include "Rank.h"
  10.  
  11. using namespace seamless;
  12. using namespace std;
  13.  
  14. namespace seamless {
  15.  
  16. void Rank::processQuery(IAPIProviderInterface* iAPIProvider) {
  17.         IParameterInterface* iParameter = iAPIProvider->getParameterInterface();
  18.         if (!iParameter) {
  19.                 cerr << "iParameter is NULL" << endl;
  20.                 return;
  21.         }
  22.  
  23.         int32_t isRetailWholesale = 0;
  24.         int32_t isUseAlipay = 0;
  25.  
  26.         VariantField* value = new VariantField;
  27.  
  28.         iParameter->getParameter("retail_wholesale", value);
  29.         isRetailWholesale = (strcmp(value->strVal, "0")) ? 1 : 0;
  30.  
  31.         iParameter->getParameter("is_use_alipay", value);
  32.         isUseAlipay = (strcmp(value->strVal, "0")) ? 1 : 0;
  33.  
  34.         cout << "isRetailWholesale:\t" << isRetailWholesale << endl;
  35.         cout << "isUseAlipay:\t" << isUseAlipay << endl;
  36.  
  37.         delete value;
  38.         delete iParameter;
  39. }
  40.  
  41. }  // namespace seamless
  • 從上面的例子中能夠看出,引擎會傳入一個IAPIProviderInterface對象,這個對象調用getParameterInterface()方法來獲得Query中的Segment。
  • 所以,咱們須要Mock的對象也比較清楚了,就是要模擬引擎將Query的Segment傳給這個模塊。其實就是讓=模擬iParameter->getParameter方法:我想讓它返回什麼樣的值就返回什麼樣的值.

下面咱們開始Mock了:
MockIParameterInterface.h 模擬模擬IParameterInterface類

  1. #ifndef MOCKIPARAMETERINTERFACE_H_
  2. #define MOCKIPARAMETERINTERFACE_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5. #include 
  6.  
  7. #include "IParameterInterface.h"
  8. #include "VariantField.h"
  9.  
  10. namespace seamless {
  11.  
  12. class MockIParameterInterface: public IParameterInterface {
  13. public:
  14.         MOCK_METHOD2(getParameter, int32_t(const char* name,  VariantField*& value));
  15. };
  16.  
  17. }  // namespace seamless
  18.  
  19. #endif // MOCKIPARAMETERINTERFACE_H_

MockIAPIProviderInterface.h 模擬IAPIProviderInterface類

  1. #ifndef MOCKIAPIPROVIDERINTERFACE_H_
  2. #define MOCKIAPIPROVIDERINTERFACE_H_
  3.  
  4. #include 
  5.  
  6. #include "IAPIProviderInterface.h"
  7. #include "IParameterInterface.h"
  8.  
  9. namespace seamless {
  10.  
  11. class MockIAPIProviderInterface: public IAPIProviderInterface{
  12. public:
  13.         MOCK_METHOD0(getParameterInterface, IParameterInterface*());
  14. };
  15.  
  16. }  // namespace seamless
  17.  
  18. #endif // MOCKIAPIPROVIDERINTERFACE_H_

tester.cc 一個測試程序,試試咱們的Mock成果

  1. #include <boost/cstdint.hpp>
  2. #include <boost/shared_ptr.hpp>
  3. #include 
  4. #include 
  5.  
  6. #include "MockIAPIProviderInterface.h"
  7. #include "MockIParameterInterface.h"
  8. #include "Rank.h"
  9.  
  10. using namespace seamless;
  11. using namespace std;
  12.  
  13. using ::testing::_;
  14. using ::testing::AtLeast;
  15. using ::testing::DoAll;
  16. using ::testing::Return;
  17. using ::testing::SetArgumentPointee;
  18.  
  19. int main(int argc, char** argv) {
  20.         ::testing::InitGoogleMock(&argc, argv);
  21.  
  22.         MockIAPIProviderInterface* iAPIProvider = new MockIAPIProviderInterface;
  23.         MockIParameterInterface* iParameter = new MockIParameterInterface;
  24.  
  25.         EXPECT_CALL(*iAPIProvider, getParameterInterface()).Times(AtLeast(1)).
  26.                 WillRepeatedly(Return(iParameter));
  27.  
  28.         boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
  29.         retailWholesaleValue->strVal = "0";
  30.  
  31.         boost::shared_ptr<VariantField> defaultValue(new VariantField);
  32.         defaultValue->strVal = "9";
  33.  
  34.         EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).
  35.                 WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).
  36.                 WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
  37.  
  38.         Rank rank;
  39.         rank.processQuery(iAPIProvider);
  40.  
  41.         delete iAPIProvider;
  42.  
  43.         return EXIT_SUCCESS;
  44. }
  • 第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,有一個模版函數

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5. #include 
  6.  
  7. #include "IBaseInterface.h"
  8. #include "IParameterInterface.h"
  9. #include "VariantField.h"
  10.  
  11. namespace seamless {
  12.  
  13. class IAPIProviderInterface: public IBaseInterface {
  14. public:
  15.         IAPIProviderInterface() {}
  16.         virtual ~IAPIProviderInterface() {}
  17.  
  18. public:
  19.         virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;
  20.  
  21.         template<typename InterfaceType>
  22.         InterfaceType* getInterface() {
  23.                 IBaseInterface* pInterface = NULL;
  24.                 if (queryInterface(pInterface)) {
  25.                         std::cerr << "Query Interface failed" << std::endl;
  26.                 }
  27.                 return static_cast<InterfaceType* >(pInterface);
  28.         }
  29. };
  30.  
  31. }
  32.  
  33. #endif // IAPIPROVIDERINTERFACE_H_

Rank.cc 既然IAPIProviderInterface.h改了,那Rank.cc中對它的調用其實也不是以前那樣的。不過其實也就差一行代碼:

  1. //      IParameterInterface* iParameter = iAPIProvider->getParameterInterface();
  2.         IParameterInterface* iParameter = iAPIProvider->getInterface<IParameterInterface>();

由於目前版本(1.5版本)的Google Mock還不支持模版函數,所以咱們沒法Mock IAPIProviderInterface中的getInterface,那咱們如今怎麼辦?
若是你想作得比較完美的話我暫時也沒想出辦法,我如今可以想出的辦法也只能這樣:IAPIProviderInterface.h 修改其中的getInterface,讓它根據模版類型,若是是IParameterInterface或者MockIParameterInterface則就返回一個MockIParameterInterface的對象

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_
  3.  
  4. #include <boost/cstdint.hpp>
  5. #include 
  6.  
  7. #include "IBaseInterface.h"
  8. #include "IParameterInterface.h"
  9. #include "VariantField.h"
  10.  
  11. // In order to Mock
  12. #include <boost/shared_ptr.hpp>
  13. #include 
  14. #include "MockIParameterInterface.h"
  15.  
  16. namespace seamless {
  17.  
  18. class IAPIProviderInterface: public IBaseInterface {
  19. public:
  20.         IAPIProviderInterface() {}
  21.         virtual ~IAPIProviderInterface() {}
  22.  
  23. public:
  24.         virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;
  25.  
  26.         template<typename InterfaceType>
  27.         InterfaceType* getInterface() {
  28.                 IBaseInterface* pInterface = NULL;
  29.                 if (queryInterface(pInterface) == 0) {
  30.                         std::cerr << "Query Interface failed" << std::endl;
  31.                 }
  32.  
  33.                 // In order to Mock
  34.                 if ((typeid(InterfaceType) == typeid(IParameterInterface)) ||
  35.                         (typeid(InterfaceType) == typeid(MockIParameterInterface))) {
  36.                         using namespace ::testing;
  37.                         MockIParameterInterface* iParameter = new MockIParameterInterface;
  38.                         boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
  39.                         retailWholesaleValue->strVal = "0";
  40.  
  41.                         boost::shared_ptr<VariantField> defaultValue(new VariantField);
  42.                         defaultValue->strVal = "9";
  43.  
  44.                         EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).
  45.                                 WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).
  46.                                 WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
  47.                         return static_cast<InterfaceType* >(iParameter);
  48.                 }
  49.                 // end of mock
  50.  
  51.                 return static_cast<InterfaceType* >(pInterface);
  52.         }
  53. };
  54.  
  55. }
  56.  
  57. #endif // IAPIPROVIDERINTERFACE_H_
  • 第33~49行,判斷傳入的模版函數的類型,而後定義相應的行爲,最後返回一個MockIParameterInterface對象

tester.cc

  1. int main(int argc, char** argv) {
  2.         ::testing::InitGoogleMock(&argc, argv);
  3.  
  4.         MockIAPIProviderInterface* iAPIProvider = new MockIAPIProviderInterface;
  5.  
  6.         InSequence dummy;
  7.         EXPECT_CALL(*iAPIProvider, queryInterface(_)).Times(AtLeast(1)).
  8.                 WillRepeatedly(Return(1));
  9.  
  10.         Rank rank;
  11.         rank.processQuery(iAPIProvider);
  12.  
  13.         delete iAPIProvider;
  14.  
  15.         return EXIT_SUCCESS;
  16. }
  • 這裏的調用就相對簡單了,只要一個MockIAPIProviderInterface就能夠了。
Google Mock Cookbook

這裏根據Google Mock Cookbook和我本身試用的一些經驗,整理一些試用方面的技巧。

Mock protected、private方法

Google Mock也能夠模擬protectedprivate方法,比較神奇啊(其實從這點上也能夠看出,Mock類不是簡單地繼承本來的接口,而後本身把它提供的方法實現;Mock類其實就等於本來的接口)。
protectedprivate方法的Mock和public基本相似,只不過在Mock類中須要將這些方法設置成public
Foo.h 帶private方法的接口

  1. class Foo {
  2. private:
  3.         virtual void setValue(int value) {};
  4.  
  5. public:
  6.         int value;
  7. };

MockFoo.h

  1. class MockFoo: public Foo {
  2. public:
  3.         MOCK_METHOD1(setValue, void(int value));
  4. };
Mock 模版類(Template Class)

Google Mock能夠Mock模版類,只要在宏MOCK*的後面加上T。
仍是相似上述那個例子:
Foo.h 改爲模版類

  1. template <typename T>
  2. class Foo {
  3. public:
  4.         virtual void setValue(int value) {};
  5.  
  6. public:
  7.         int value;
  8. };

MockFoo.h

  1. template <typename T>
  2. class Foo {
  3. public:
  4.         virtual void setValue(int value) {};
  5.  
  6. public:
  7.         int value;
  8. };
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來避免:

  1.         // MockFoo mockFoo;
  2.         NiceMock<MockFoo> mockFoo;
使用NiceMock來替代以前的MockFoo。

固然,另外還有一種辦法,就是使用StrictMock來將這些調用都標爲失敗:

  1. StrictMock<MockFoo> mockFoo;

這時獲得的結果:

unknown file: Failure
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0
 
標籤:  google mock
相關文章
相關標籤/搜索