生活随笔
收集整理的這篇文章主要介紹了
用google mock模拟C++对象
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
google mock是用來配合google test對C++項目做單元測試的。它依賴于googletest(參見我上篇文章《如何用googletest寫單元測試》:?http://blog.csdn.net/russell_tao/article/details/7333226),下面我來說說linux上怎么用它來做單元測試。
本文包括:1、如何獲取、編譯google mock;2、如何使用gmock(下面用gmock來代稱google mock)配合gtest做單元測試。
1、如何獲取、編譯google mock
gmock的當前版本與gtest一樣,是1.6.0。可以從這個網址獲取:http://code.google.com/p/googlemock/downloads/list。
下載到壓縮包解壓后,下面我們開始編譯出靜態庫文件(必須得自己編譯出),以在我們自己的單元測試工程中使用。
與gtest相同,我們執行完./configure; make后,不能執行make install,理由與上篇相同。
驗證這個包有沒有問題,依然可以執行如下命令:
[cpp] ?view plaincopy
cd?make?? make?? ./gmock_test??
如果你看到類似下文的輸出屏幕,證明你的機器運行gmock沒有問題。
[cpp] ?view plaincopy
[----------]?Global?test?environment?tear-down?? [==========]?13?tests?from?3?test?cases?ran.?(2?ms?total)?? [??PASSED??]?13?tests.??
這時還沒有編譯出我們要的libgmock.a呢。繼續在gmock解包目錄下執行:
[cpp] ?view plaincopy
g++?-I?gtest/include/?-I?gtest/?-I?include/?-I?./?-c?gtest/src/gtest-all.cc??? g++?-I?gtest/include/?-I?gtest/?-I?include/?-I?./?-c?src/gmock-all.cc????????? ar?-rv?libgmock.a?gtest-all.o?gmock-all.o???
如此,當前目錄下會鏈接出我們需要的libgmock.a。注意,這個gmock.a靜態庫里,把gtest需要的gtest-all.cc都編譯進來了,所以我們的單元測試工程只需要鏈接libgmock,不再需要鏈接上文說的libgtest了。
2、如何使用gmock
首先,編譯我們自己的單元測試工程時,需要在makefile里加入以下編譯選項:-I${GTEST_DIR}/include -I${GMOCK_DIR}/include,這兩個目錄我們自己從上面的包里拷貝出來即可。鏈接時,需要加上libgmock.a。
還是以一個例子來說明怎么在mock對象的情況下寫單元測試。
我現在有一個生產者消費者網絡模型,消費者(例如client)會先發TCP請求到我的SERVER去訂閱某個對象。生產者(另一臺SERVER)產生關于某個對象的事件后發給我的SERVER后,我的SERVER再把事件發給消費者。
就是這么簡單。
我現在想寫一個單元測試,主要測試代碼邏輯,不想去管網絡包的收發這些事情。
我現在有兩個類,一個叫CSubscriber,它封裝為一個訂閱的消費者,功能主要是操作網絡,包括網絡收發包,協議解析等。另一個叫CSubEventHandler,它主要做邏輯處理,去操作CSubscriber對象,例如epoll返回讀事件后,會構造一個CSubscriber對象,然后CSubEventHandler::handleRead方法就來處理這個CSubscriber對象。
我單元測試的目的是,測試CSubEventHandler::handleRead的業務邏輯,我同時也想測試CSubscriber方法里的協議解析邏輯,但是對于CSubscriber封裝的讀寫包部分,我希望可以mock成我想要的網絡包。
怎么做呢?
a)、先mock一個CSubscriber類如下:
[cpp] ?view plaincopy
class ?MockCSubscriber?:? public ?CSubscriber?? {?? public :?? ????MockCSubscriber(int ?fd):CSubscriber(fd){}?? ????MOCK_METHOD1(readBuf,?int ( int ?len));?? ????MOCK_METHOD1(writeBuf,?int ( int ?len));?? ????MOCK_METHOD0(closeSock,?void ());?? };??
其中,CSubscriber的構造方法必須有一個int型的fd,而readBuf和writeBuf都只接收一個int型的參數,而closeSock方法 沒有參數傳遞。于是我使用了MOCK_METHOD0和MOCK_METHOD1這兩個宏來聲明想MOCK的方法。這兩個宏的使用很簡單,解釋下:
MOCK_METHOD#1(#2, #3(#4) )
#2是你要mock的方法名稱!#1表示你要mock的方法共有幾個參數,#4是這個方法具體的參數,#3表示這個方法的返回值類型。
很簡單不是?!
b)、如果只關心mock方法的返回值。
這里用到一個宏ON_CALL。看例子:
[cpp] ?view plaincopy
ON_CALL(subObj,?readBuf(1000)).WillByDefault(Return(blen));??
什么意思呢?再用剛才的解釋方法:
ON_CALL(#1, #2(#3)).WillByDefault(Return(#4));
#1表示mock對象 。就像我上面所說,對CSubscriber我定義了一個Mock類,那么就必須生成相應的mock對象,例如:
[cpp] ?view plaincopy
MockCSubscriber?subObj(5);??
#2表示想定義的那個方法名稱 。上例中我想定義readBuf這個方法的返回值。
#3表示readBuf方法的參數 。這里的1000表示,只有調用CSubscriber::readBuf同時傳遞參數為1000時,才會用到ON_CALL的定義。
#4表示調用CSubscriber::readBuf同時傳遞參數為1000時,返回blen這個變量的值 。
c)、如果還希望mock方法有固定的被調用方式
這里用到宏EXPECT_CALL,看個例子:
[cpp] ?view plaincopy
EXPECT_CALL(subObj,?readBuf(1000)).Times(1);??
很相似吧?最后的Times表示,只希望readBuf在傳遞參數為1000時,被調用且僅被調用一次。
其實這些宏有很復雜的用法的,例如:
[cpp] ?view plaincopy
EXPECT_CALL(subObj,?readBuf(1000))?? ????.Times(5)?? ????.WillOnce(Return(100))?? ????.WillOnce(Return(150))?? ????.WillRepeatedly(Return(200));??
表示,readBuf希望被調用五次,第一次返回100,第二次返回150,后三次返回200。如果不滿足,會報錯。
d)、實際的調用測試
其實調用跟上篇googletest文章里的測試是一致的,我這里只列下上文的完整用例代碼(不包括被測試類的實現代碼):
[cpp] ?view plaincopy
#include?"gtest/gtest.h" ?? #include?"gmock/gmock.h" ?? #include?"CSubscriber.h" ?? #include?"CPublisher.h" ?? #include?"CSubEventHandler.h" ?? #include?"CPubEventHandler.h" ?? ?? using ?::testing::AtLeast;?? using ?testing::Return;?? ?? ?? class ?MockCSubscriber?:? public ?CSubscriber?? {?? public :?? ????MockCSubscriber(int ?fd):CSubscriber(fd){}?? ????MOCK_METHOD1(readBuf,?int ( int ?len));?? ????MOCK_METHOD1(writeBuf,?int ( int ?len));?? ????MOCK_METHOD0(closeSock,?void ());?? };?? ?? class ?MockCPublisher?:? public ?CPublisher?? {?? public :?? ????MockCPublisher(int ?fd):CPublisher(fd){}?? ????MOCK_METHOD1(readBuf,?int ( int ?len));?? ????MOCK_METHOD1(writeBuf,?int ( int ?len));?? ????MOCK_METHOD0(closeSock,?void ());?? };?? ?? ?? TEST(subpubHandler,?sub1pub1)?{?? ????MockCSubscriber?subObj(5);?? ????MockCPublisher?pubObj(5);?? ?? ????subObj.m_iRecvBufLen?=?1000;?? ????pubObj.m_iRecvBufLen?=?1000;?? ?? ????char *?pSubscribeBuf?=? "GET?/?HTTP/1.1\r\nobject:?/tt/aa\r\ntime:?112\r\n\r\n" ;?? ????char *?pMessageBuf?=? "GET?/?HTTP/1.1\r\nobject:?/tt/aa\r\ntime:?112\r\nmessage:?tttt\r\n\r\n" ;?? ????subObj.m_pRecvBuf?=?pSubscribeBuf;?? ????int ?blen?=?strlen(pSubscribeBuf);?? ????subObj.m_iRecvPos?=?blen;?? ?? ????pubObj.m_pRecvBuf?=?pMessageBuf;?? ????int ?mlen?=?strlen(pMessageBuf);?? ????pubObj.m_iRecvPos?=?mlen;?? ?? ?? ????ON_CALL(subObj,?readBuf(1000)).WillByDefault(Return(blen));?? ????ON_CALL(subObj,?writeBuf(CEventHandler::InternalError.size())).WillByDefault(Return(0));?? ?? ????CSubEventHandler?subHandler(NULL);?? ????CPubEventHandler?pubHandler(NULL);?? ?? ????CHashTable?ht1(100);?? ????CHashTable?ht2(100);?? ????subHandler.initial(100,?&ht1,?&ht2);?? ????pubHandler.initial(100,?&ht1,?&ht2);?? ?? ????EXPECT_CALL(subObj,?readBuf(1000)).Times(1);?? ?????? ????EXPECT_CALL(subObj,?writeBuf(4)).Times(1);?? ?? ????EXPECT_TRUE(subHandler.handleRead(&subObj));?? ?? ????ON_CALL(pubObj,?readBuf(1000)).WillByDefault(Return(mlen));?? ????ON_CALL(pubObj,?writeBuf(4)).WillByDefault(Return(0));?? ?? ????EXPECT_CALL(pubObj,?readBuf(1000)).Times(1);?? ????EXPECT_CALL(pubObj,?closeSock()).Times(1);?? ????EXPECT_CALL(pubObj,?writeBuf(CEventHandler::Success.size())).Times(1);?? ?? ????EXPECT_TRUE(pubHandler.handleRead(&pubObj));?? }??
CSubscriber的頭文件:
[cpp] ?view plaincopy
class ?CSubscriber?:? public ?CBaseConnection,? public ?CHashElement?? {?? public :?? ????CSubscriber(int ?fd);?? ?????? ????virtual ?~CSubscriber();?? ?? ????bool ?initial();?? ?? ????bool ?reset();?? ?? ?????? ?????? ?????? ?????? ?????? ????int ?readPacket();?? ?? ?????? ????static ? int ?m_iSendBufLen;?? ?? ?????? ????static ? int ?m_iRecvBufLen;?? ?? private :?? ????? ?? ????bool ?parsePacket();?? };??
e)、main函數的寫法
與gtest相同,唯一的區別是初始化參數,如下:
[cpp] ?view plaincopy
#include?<gmock/gmock.h> ?? ?? int ?main( int ?argc,? char **?argv)?{?? ????testing::InitGoogleMock(&argc,?argv);?? ?????? ?? ?????? ????return ?RUN_ALL_TESTS();?? }??
如此,就可以完整的使用googletest/googlemock做C++工程的單元測試了,確實很簡單好用。
總結
以上是生活随笔 為你收集整理的用google mock模拟C++对象 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。