日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

google mock分享

發(fā)布時(shí)間:2024/2/28 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 google mock分享 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Content
  • Matcher(匹配器)
  • 基數(shù)(Cardinalities)
  • 行為(Actions)
  • 序列(Sequences)
  • Google Mock 入門
  • 概述
  • Google Mock使用
  • Mock實(shí)踐
  • Google Mock Cookbook
  • 什么是Mock?
  • Google Mock概述
  • 參考文檔
  • 最簡(jiǎn)單的例子
  • 典型的流程
  • 自定義方法/成員函數(shù)的期望行為
  • 我改過(guò)的例子
  • 現(xiàn)實(shí)中的例子
  • Mock protected、private方法
  • Mock 模版類(Template Class)
  • Nice Mocks 和 Strict Mocks

Google Mock 入門

概述

什么是Mock?

Mock,更確切地說(shuō)應(yīng)該是Mock Object。它究竟是什么?它有什么作用?在這里,我也只能先說(shuō)說(shuō)我的理解。 比如當(dāng)我們?cè)趩卧獪y(cè)試、模塊的接口測(cè)試時(shí),當(dāng)這個(gè)模塊需要依賴另外一個(gè)/幾個(gè)類,而這時(shí)這些個(gè)類還沒(méi)有開(kāi)發(fā)好(那名開(kāi)發(fā)同學(xué)比較懶,呵呵),這時(shí)我們就可以定義了Mock對(duì)象來(lái)模擬那些類的行為。
說(shuō)得更直白一些,就是自己實(shí)現(xiàn)一個(gè)假的依賴類,對(duì)這個(gè)類的方法你想要什么行為就可以有什么行為,你想讓這個(gè)方法返回什么結(jié)果就可以返回怎么樣的結(jié)果。
但這時(shí)很多同學(xué)往往會(huì)提出一個(gè)問(wèn)題:"那既然是我自己實(shí)現(xiàn)一個(gè)假的依賴類",那和那些市面上的Mock框架有什么關(guān)系啊?
這個(gè)其實(shí)是這樣的,這些個(gè)Mock框架可以幫助你比較方便、比較輕松地實(shí)現(xiàn)這些個(gè)假的依賴類。畢竟,如果你實(shí)現(xiàn)這么一個(gè)假的依賴類的時(shí)間花費(fèi)過(guò)場(chǎng)的話,那我還不如等待那位懶惰的同學(xué)吧。

Google Mock概述

Google Mock(簡(jiǎn)稱gmock)是Google在2008年推出的一套針對(duì)C++的Mock框架,它靈感取自于jMock、EasyMock、harcreat。它提供了以下這些特性:

  • 輕松地創(chuàng)建mock類
  • 支持豐富的匹配器(Matcher)和行為(Action)
  • 支持有序、無(wú)序、部分有序的期望行為的定義
  • 多平臺(tái)的支持

參考文檔

  • 新人手冊(cè)
  • Cheat Sheet
  • Cheat Sheet中文翻譯
  • Cookbook

Google Mock使用

最簡(jiǎn)單的例子

我比較喜歡舉例來(lái)說(shuō)明這些個(gè)、那些個(gè)玩意,因此我們先來(lái)看看Google Mock就簡(jiǎn)單的用法和作用。

  • 首先,那個(gè)懶惰的同學(xué)已經(jīng)定義好了這么一個(gè)接口(萬(wàn)幸,他至少把接口定義好了):

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_


這里需要注意幾點(diǎn):

  • FooInterface的析構(gòu)函數(shù)~FooInterface()必須是virtual的
  • 在第13行,我們得把getArbitraryString定義為純虛函數(shù)。其實(shí)getArbitraryString()也不一定得是純虛函數(shù),這點(diǎn)我們后面會(huì)提到.

現(xiàn)在我們用Google Mock來(lái)定義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_

我們稍微來(lái)解釋一下這個(gè)Mock類的定義:

  • 第10行我們的MockFoo類繼承懶同學(xué)的FooInterface
  • 第22行我們定義使用gmock中的一個(gè)宏(Macro)MOCK_METHOD0來(lái)定義MockFoo中的getArbitraryString。Google Mock是需要你根據(jù)不同的形參個(gè)數(shù)來(lái)使用不同的Mock Method,我這里getArbitraryString沒(méi)有函數(shù),就是MOCK_METHOD0了,同理,如果是一個(gè)形參,就是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; }


最后我們運(yùn)行編譯,得到的結(jié)果如下:

Returned Value: Hello World!

在這里:

  • 第15行,初始化一個(gè)Google Mock
  • 第18行,聲明一個(gè)MockFoo的對(duì)象:mockFoo
  • 第19行,是為MockFoo的getArbitraryString()方法定義一個(gè)期望行為,其中Times(1)的意思是運(yùn)行一次,WillOnce(Return(value))的意思是第一次運(yùn)行時(shí)把value作為getArbitraryString()方法的返回值。

這就是我們最簡(jiǎn)單的使用Google Mock的例子了,使用起來(lái)的確比較簡(jiǎn)便吧。

典型的流程

通過(guò)上述的例子,已經(jīng)可以看出使用Mock類的一般流程如下:

  • 引入你要用到的Google Mock名稱. 除宏或其它特別提到的之外所有Google Mock名稱都位于*testing*命名空間之下.
  • 建立模擬對(duì)象(Mock Objects).
  • 可選的,設(shè)置模擬對(duì)象的默認(rèn)動(dòng)作.
  • 在模擬對(duì)象上設(shè)置你的預(yù)期(它們?cè)鯓颖徽{(diào)用,應(yīng)該怎樣回應(yīng)?).

自定義方法/成員函數(shù)的期望行為

從上述的例子中可以看出,當(dāng)我們針對(duì)懶同學(xué)的接口定義好了Mock類后,在單元測(cè)試/主程序中使用這個(gè)Mock類中的方法時(shí)最關(guān)鍵的就是對(duì)期望行為的定義。
對(duì)方法期望行為的定義的語(yǔ)法格式如下:

EXPECT_CALL(mock_object, method(matcher1, matcher2, ...)).With(multi_argument_matcher).Times(cardinality).InSequence(sequences).After(expectations).WillOnce(action).WillRepeatedly(action).RetiresOnSaturation();

解釋一下這些參數(shù)(雖然很多我也沒(méi)弄明白):

  • 第1行的mock_object就是你的Mock類的對(duì)象
  • 第1行的method(matcher1, matcher2, …)中的method就是你Mock類中的某個(gè)方法名,比如上述的getArbitraryString;而matcher(匹配器)的意思是定義方法參數(shù)的類型,我們待會(huì)詳細(xì)介紹。
  • 第3行的Times(cardinality)的意思是之前定義的method運(yùn)行幾次。至于cardinality的定義,我也會(huì)在后面詳細(xì)介紹。
  • 第4行的InSequence(sequences)的意思是定義這個(gè)方法被執(zhí)行順序(優(yōu)先級(jí)),我會(huì)再后面舉例說(shuō)明。
  • 第6行WillOnce(action)是定義一次調(diào)用時(shí)所產(chǎn)生的行為,比如定義該方法返回怎么樣的值等等。
  • 第7行WillRepeatedly(action)的意思是缺省/重復(fù)行為。

我稍微先舉個(gè)例子來(lái)說(shuō)明一下,后面有針對(duì)更為詳細(xì)的說(shuō)明:

  • EXPECT_CALL(mockTurtle, getX()).Times(testing::AtLeast(5)).
  • ? ? ? ? ? ? ? ?WillOnce(testing::Return(100)).WillOnce(testing::Return(150)).
  • ? ? ? ? ? ? ? ?WillRepeatedly(testing::Return(200))

  • 這個(gè)期望行為的定義的意思是:

    • 調(diào)用mockTurtle的getX()方法
    • 這個(gè)方法會(huì)至少調(diào)用5次
    • 第一次被調(diào)用時(shí)返回100
    • 第2次被調(diào)用時(shí)返回150
    • 從第3次被調(diào)用開(kāi)始每次都返回200

    Matcher(匹配器)

    Matcher用于定義Mock類中的方法的形參的值(當(dāng)然,如果你的方法不需要形參時(shí),可以保持match為空。),它有以下幾種類型:(更詳細(xì)的介紹可以參見(jiàn)Google Mock Wiki上的Matcher介紹)
    通配符

    _ 可以代表任意類型
    A() or An() 可以是type類型的任意值
    這里的_和*A*包括下面的那個(gè)匹配符都在Google Mock的*::testing*這個(gè)命名空間下,大家要用時(shí)需要先引入那個(gè)命名空間

    一般比較

    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

    浮點(diǎn)數(shù)的比較

    DoubleEq(a_double) 形參是一個(gè)double類型,比如值近似于a_double,兩個(gè)NaN是不相等的
    FloatEq(a_float) 同上,只不過(guò)類型是float
    NanSensitiveDoubleEq(a_double) 形參是一個(gè)double類型,比如值近似于a_double,兩個(gè)NaN是相等的,這個(gè)是用戶所希望的方式
    NanSensitiveFloatEq(a_float) 同上,只不過(guò)形參是float

    字符串匹配
    這里的字符串即可以是C風(fēng)格的字符串,也可以是C++風(fēng)格的。

    ContainsRegex(string) 形參匹配給定的正則表達(dá)式
    EndsWith(suffix) 形參以suffix截尾
    HasSubstr(string) 形參有string這個(gè)子串
    MatchesRegex(string) 從第一個(gè)字符到最后一個(gè)字符都完全匹配給定的正則表達(dá)式.
    StartsWith(prefix) 形參以prefix開(kāi)始
    StrCaseEq(string) 參數(shù)等于string,并且忽略大小寫
    StrCaseNe(string) 參數(shù)不是string,并且忽略大小寫
    StrEq(string) 參數(shù)等于string
    StrNe(string) 參數(shù)不等于string

    容器的匹配
    很多STL的容器的比較都支持==這樣的操作,對(duì)于這樣的容器可以使用上述的Eq(container)來(lái)比較。但如果你想寫得更為靈活,可以使用下面的這些容器匹配方法:

    Contains(e) 在method的形參中,只要有其中一個(gè)元素等于e
    Each(e) 參數(shù)各個(gè)元素都等于e
    ElementsAre(e0, e1, …, en) 形參有n+1的元素,并且挨個(gè)匹配
    ElementsAreArray(array) 或者ElementsAreArray(array, count) 和ElementsAre()類似,除了預(yù)期值/匹配器來(lái)源于一個(gè)C風(fēng)格數(shù)組
    ContainerEq(container) 類型Eq(container),就是輸出結(jié)果有點(diǎn)不一樣,這里輸出結(jié)果會(huì)帶上哪些個(gè)元素不被包含在另一個(gè)容器中
    Pointwise(m, container) ?

    上述的一些匹配器都比較簡(jiǎn)單,我就隨便打包舉幾最簡(jiǎn)單的例子演示一下吧: 我稍微修改一下之前的Foo.hMockFoo.h,?MockFoo.h?增加了2個(gè)方法

    • #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);// 這里我故意犯錯(cuò)EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)));mockFoo.setDoubleValues(1, 0);return EXIT_SUCCESS; }


      • 第22行,讓setValue的形參可以傳入任意參數(shù)
      • 另外,我在第26~27行故意犯了個(gè)錯(cuò)(為了說(shuō)明上述這些匹配器的作用),我之前明明讓setDoubleValues第二個(gè)參數(shù)得大于等于1,但我實(shí)際傳入時(shí)卻傳入一個(gè)0。這時(shí)程序運(yùn)行時(shí)就報(bào)錯(cuò)了:
      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

      上述的那些匹配器都比較簡(jiǎn)單,下面我們來(lái)看看那些比較復(fù)雜的匹配吧。
      成員匹配器

      Field(&class::field, m) argument.field (或 argument->field, 當(dāng)argument是一個(gè)指針時(shí))與匹配器m匹配, 這里的argument是一個(gè)class類的實(shí)例.
      Key(e) 形參(argument)比較是一個(gè)類似map這樣的容器,然后argument.first的值等于e
      Pair(m1, m2) 形參(argument)必須是一個(gè)pair,并且argument.first等于m1,argument.second等于m2.
      Property(&class::property, m) argument.property()(或argument->property(),當(dāng)argument是一個(gè)指針時(shí))與匹配器m匹配, 這里的argument是一個(gè)class類的實(shí)例.

      還是舉例說(shuō)明一下:

      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來(lái)寫個(gè)測(cè)試用例,這樣看得比較清楚。

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

      上面這個(gè)是正確的例子,我們?yōu)榱苏f(shuō)明Field的作用,傳入一個(gè)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); }

      運(yùn)行是出錯(cuò)了:

      [==========] 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

      匹配函數(shù)或函數(shù)對(duì)象的返回值

      ResultOf(f, m) f(argument) 與匹配器m匹配, 這里的f是一個(gè)函數(shù)或函數(shù)對(duì)象.

      指針匹配器

      Pointee(m) argument (不論是智能指針還是原始指針) 指向的值與匹配器m匹配.

      復(fù)合匹配器

      AllOf(m1, m2, …, mn) argument 匹配所有的匹配器m1到mn
      AnyOf(m1, m2, …, mn) argument 至少匹配m1到mn中的一個(gè)
      Not(m) argument 不與匹配器m匹配
      EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));

      • 傳入的參數(shù)必須 >5 并且 <= 1EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
      • 第一個(gè)參數(shù)不包含“blah”這個(gè)子串

      基數(shù)(Cardinalities)

      基數(shù)用于Times()中來(lái)指定模擬函數(shù)將被調(diào)用多少次|

      AnyNumber() 函數(shù)可以被調(diào)用任意次.
      AtLeast(n) 預(yù)計(jì)至少調(diào)用n次.
      AtMost(n) 預(yù)計(jì)至多調(diào)用n次.
      Between(m, n) 預(yù)計(jì)調(diào)用次數(shù)在m和n(包括n)之間.
      Exactly(n) 或 n 預(yù)計(jì)精確調(diào)用n次. 特別是, 當(dāng)n為0時(shí),函數(shù)應(yīng)該永遠(yuǎn)不被調(diào)用.

      行為(Actions)

      Actions(行為)用于指定Mock類的方法所期望模擬的行為:比如返回什么樣的值、對(duì)引用、指針賦上怎么樣個(gè)值,等等。?值的返回

      Return() 讓Mock方法返回一個(gè)void結(jié)果
      Return(value) 返回值value
      ReturnNull() 返回一個(gè)NULL指針
      ReturnRef(variable) 返回variable的引用.
      ReturnPointee(ptr) 返回一個(gè)指向ptr的指針

      另一面的作用(Side Effects)

      Assign(&variable, value) 將value分配給variable

      使用函數(shù)或者函數(shù)對(duì)象(Functor)作為行為

      Invoke(f) 使用模擬函數(shù)的參數(shù)調(diào)用f, 這里的f可以是全局/靜態(tài)函數(shù)或函數(shù)對(duì)象.
      Invoke(object_pointer, &class::method) 使用模擬函數(shù)的參數(shù)調(diào)用object_pointer對(duì)象的mothod方法.

      復(fù)合動(dòng)作

      DoAll(a1, a2, …, an) 每次發(fā)動(dòng)時(shí)執(zhí)行a1到an的所有動(dòng)作.
      IgnoreResult(a) 執(zhí)行動(dòng)作a并忽略它的返回值. a不能返回void.

      這里我舉個(gè)例子來(lái)解釋一下DoAll()的作用,我個(gè)人認(rèn)為這個(gè)DoAll()還是挺實(shí)用的。例如有一個(gè)Mock方法:

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

      對(duì)于這個(gè)方法,我這回需要操作的結(jié)果是將name指向value的地址,并且得到方法的返回值。
      類似這樣的需求,我們就可以這樣定義期望過(guò)程:

      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); }


      這時(shí)就用上了我們的DoAll()了,它將Assign()和Return()結(jié)合起來(lái)了。

      序列(Sequences)

      默認(rèn)時(shí),對(duì)于定義要的期望行為是無(wú)序(Unordered)的,即當(dāng)我定義好了如下的期望行為:

    • MockFoo mockFoo;EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
    • 對(duì)于這樣的期望行為的定義,我何時(shí)調(diào)用mockFoo.getValue()或者何時(shí)mockFoo.getSize()都可以的。

      但有時(shí)候我們需要定義有序的(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; }

      • 首先在第8行建立兩個(gè)序列:s1、s2。
      • 然后在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)說(shuō)明getSize()的行為優(yōu)先于s1、s2.
      • 而第12行時(shí),EXPECT_CALL(mockFoo, getValue()).InSequence(s1)說(shuō)明getValue()的行為在序列s1中。

      得到的結(jié)果如下:

      First: 1
      Second: Hello World!

      當(dāng)我嘗試一下把mockFoo.getSize()mockFoo.getValue()的調(diào)用對(duì)調(diào)時(shí)試試:

      cout << "Second:\t" << mockFoo.getValue() << endl;cout << "First:\t" << mockFoo.getSize() << endl;

      得到如下的錯(cuò)誤信息:

      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

      另外,我們還有一個(gè)偷懶的方法,就是不要這么傻乎乎地定義這些個(gè)Sequence s1, s2的序列,而根據(jù)我定義期望行為(EXPECT_CALL)的順序而自動(dòng)地識(shí)別調(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實(shí)踐

      下面我從我在工作中參與的項(xiàng)目中選取了一個(gè)實(shí)際的例子來(lái)實(shí)踐Mock。
      這個(gè)例子的背景是用于搜索引擎的:

      • 引擎接收一個(gè)查詢的Query,比如http://127.0.0.1/search?q=mp3&retailwholesale=0&isuse_alipay=1
      • 引擎接收到這個(gè)Query后,將解析這個(gè)Query,將Query的Segment(如q=mp3、retail_wholesale=0放到一個(gè)數(shù)據(jù)結(jié)構(gòu)中)
      • 引擎會(huì)調(diào)用另外內(nèi)部模塊具體根據(jù)這些Segment來(lái)處理相應(yīng)的業(yè)務(wù)邏輯。

      由于Google Mock不能Mock模版方法,因此我稍微更改了一下原本的接口,以便演示:

      我改過(guò)的例子

      我們先來(lái)看看引擎定義好的接口們:
      VariantField.h?一個(gè)聯(lián)合體,用于保存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?提供一個(gè)接口,用于得到Query中的各個(gè)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?一個(gè)統(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_

      引擎定義好的接口就以上三個(gè),下面是引擎中的一個(gè)模塊用于根據(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?實(shí)現(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

      • 從上面的例子中可以看出,引擎會(huì)傳入一個(gè)IAPIProviderInterface對(duì)象,這個(gè)對(duì)象調(diào)用getParameterInterface()方法來(lái)得到Query中的Segment。
      • 因此,我們需要Mock的對(duì)象也比較清楚了,就是要模擬引擎將Query的Segment傳給這個(gè)模塊。其實(shí)就是讓=模擬iParameter->getParameter方法:我想讓它返回什么樣的值就返回什么樣的值.

      下面我們開(kāi)始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?一個(gè)測(cè)試程序,試試我們的Mock成果

      #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; }

      • 第26行,定義一個(gè)執(zhí)行順序,因此在之前的Rank.cc中,是先調(diào)用iAPIProvider>getParameterInterface,然后再調(diào)用iParameter>getParameter,因此我們?cè)谙旅鏁?huì)先定義MockIAPIProviderInterface.getParameterInterface的期望行為,然后再是其他的。
      • 第27~28行,定義MockIAPIProviderInterface.getParameterInterface的的行為:程序至少被調(diào)用一次(Times(AtLeast(1))),每次調(diào)用都返回一個(gè)iParameter(即MockIParameterInterface*的對(duì)象)。
      • 第30~34行,我自己假設(shè)了一些Query的Segment的值。即我想達(dá)到的效果是Query類似http://127.0.0.1/search?retailwholesale=0&isuse_alipay=9。
      • 第36~38行,我們定義MockIParameterInterface.getParameter的期望行為:這個(gè)方法至少被調(diào)用一次;第一次被調(diào)用時(shí)返回1并將第一個(gè)形參指向retailWholesaleValue;后續(xù)幾次被調(diào)用時(shí)返回1,并指向defaultValue。
      • 第51行,運(yùn)行Rank類下的processQuery方法。

      看看我們的運(yùn)行成果:

      isRetailWholesale: 0
      isUseAlipay: 1

      從這個(gè)結(jié)果驗(yàn)證出我們傳入的Query信息是對(duì)的,成功Mock!

      現(xiàn)實(shí)中的例子

      就如我之前所說(shuō)的,上述的那個(gè)例子是我改過(guò)的,現(xiàn)實(shí)項(xiàng)目中哪有這么理想的結(jié)構(gòu)(特別對(duì)于那些從來(lái)沒(méi)有Develop for Debug思想的同學(xué))。
      因此我們來(lái)看看上述這個(gè)例子中實(shí)際的代碼:其實(shí)只有IAPIProviderInterface.h不同,它定義了一個(gè)模版函數(shù),用于統(tǒng)一各種類型的接口:?IAPIProviderInterface.h?真正的IAPIProviderInterface.h,有一個(gè)模版函數(shù)

      #ifndef IAPIPROVIDERINTERFACE_H_ #define IAPIPROVIDERINTERFACE_H_#include <boost/cstdint.hpp> #include <iostream>#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中對(duì)它的調(diào)用其實(shí)也不是之前那樣的。不過(guò)其實(shí)也就差一行代碼:// IParameterInterface* iParameter = iAPIProvider->getParameterInterface();IParameterInterface* iParameter = iAPIProvider->getInterface<IParameterInterface>();因?yàn)槟壳鞍姹?#xff08;1.5版本)的Google Mock還不支持模版函數(shù),因此我們無(wú)法Mock IAPIProviderInterface中的getInterface,那我們現(xiàn)在怎么辦?
      如果你想做得比較完美的話我暫時(shí)也沒(méi)想出辦法,我現(xiàn)在能夠想出的辦法也只能這樣:IAPIProviderInterface.h?修改其中的getInterface,讓它根據(jù)模版類型,如果是IParameterInterface或者MockIParameterInterface則就返回一個(gè)MockIParameterInterface的對(duì)象#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_

      • 第33~49行,判斷傳入的模版函數(shù)的類型,然后定義相應(yīng)的行為,最后返回一個(gè)MockIParameterInterface對(duì)象

      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; }

      • 這里的調(diào)用就相對(duì)簡(jiǎn)單了,只要一個(gè)MockIAPIProviderInterface就可以了。

      Google Mock Cookbook

      這里根據(jù)Google Mock Cookbook和我自己試用的一些經(jīng)驗(yàn),整理一些試用方面的技巧。

      Mock protected、private方法

      Google Mock也可以模擬protectedprivate方法,比較神奇啊(其實(shí)從這點(diǎn)上也可以看出,Mock類不是簡(jiǎn)單地繼承原本的接口,然后自己把它提供的方法實(shí)現(xiàn);Mock類其實(shí)就等于原本的接口)。
      對(duì)protectedprivate方法的Mock和public基本類似,只不過(guò)在Mock類中需要將這些方法設(shè)置成public
      Foo.h?帶private方法的接口

      class Foo { private:virtual void setValue(int value) {};public:int value; };MockFoo.h

      <pre name="code" class="cpp">class MockFoo: public Foo { public:MOCK_METHOD1(setValue, void(int value)); };

      Mock 模版類(Template Class)

      Google Mock可以Mock模版類,只要在宏MOCK*的后面加上T。
      還是類似上述那個(gè)例子:
      Foo.h?改成模版類

      template <typename T> class Foo { public:virtual void setValue(int value) {};public:int value; };MockFoo.htemplate <typename T> class Foo { public:virtual void setValue(int value) {};public:int value; };

      Nice Mocks 和 Strict Mocks

      當(dāng)在調(diào)用Mock類的方法時(shí),如果之前沒(méi)有使用EXPECT_CALL來(lái)定義該方法的期望行為時(shí),Google Mock在運(yùn)行時(shí)會(huì)給你一些警告信息:

      GMOCK WARNING:
      Uninteresting mock function call – returning default value.
      Function call: setValue(1)
      Returns: 0
      Stack trace

      對(duì)于這種情況,可以使用NiceMock來(lái)避免:

      // MockFoo mockFoo;NiceMock<MockFoo> mockFoo;

      使用NiceMock來(lái)替代之前的MockFoo。

      當(dāng)然,另外還有一種辦法,就是使用StrictMock來(lái)將這些調(diào)用都標(biāo)為失敗:

      StrictMock<MockFoo> mockFoo;
      這時(shí)得到的結(jié)果:

      unknown file: Failure
      Uninteresting mock function call – returning default value.
      Function call: setValue(1)
      Returns: 0 ?


    總結(jié)

    以上是生活随笔為你收集整理的google mock分享的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。