用google mock模擬C++對象

google mock是用來配合google test對C++項目作單元測試的。它依賴於googletest(參見我上篇文章《如何用googletest寫單元測試》: http://blog.csdn.net/russell_tao/article/details/7333226),下面我來講說Linux上怎麼用它來作單元測試。linux

本文包括:一、如何獲取、編譯google mock;二、如何使用gmock(下面用gmock來代稱google mock)配合gtest作單元測試。網絡

 

一、如何獲取、編譯google mock函數

gmock的當前版本與gtest同樣,是1.6.0。能夠從這個網址獲取:http://code.google.com/p/googlemock/downloads/list單元測試

下載到壓縮包解壓後,下面咱們開始編譯出靜態庫文件(必須得本身編譯出),以在咱們本身的單元測試工程中使用。測試

與gtest相同,咱們執行完./configure; make後,不能執行make install,理由與上篇相同。google

驗證這個包有沒有問題,依然能夠執行以下命令:spa

 

[cpp]  view plain  copy
 
  1. cd make  
  2. make  
  3. ./gmock_test  


若是你看到相似下文的輸出屏幕,證實你的機器運行gmock沒有問題。.net

 

 

[cpp]  view plain  copy
 
  1. [----------] Global test environment tear-down  
  2. [==========] 13 tests from 3 test cases ran. (2 ms total)  
  3. [  PASSED  ] 13 tests.  


這時尚未編譯出咱們要的libgmock.a呢。繼續在gmock解包目錄下執行:code

 

 

[cpp]  view plain  copy
 
  1. g++ -I gtest/include/ -I gtest/ -I include/ -I ./ -c gtest/src/gtest-all.cc   
  2. g++ -I gtest/include/ -I gtest/ -I include/ -I ./ -c src/gmock-all.cc         
  3. ar -rv libgmock.a gtest-all.o gmock-all.o   


如此,當前目錄下會連接出咱們須要的libgmock.a。注意,這個gmock.a靜態庫裏,把gtest須要的gtest-all.cc都編譯進來了,因此咱們的單元測試工程只須要連接libgmock,再也不須要連接上文說的libgtest了。orm

 

 

二、如何使用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 plain  copy
 
  1. class MockCSubscriber : public CSubscriber  
  2. {  
  3. public:  
  4.     MockCSubscriber(int fd):CSubscriber(fd){}  
  5.     MOCK_METHOD1(readBuf, int(int len));  
  6.     MOCK_METHOD1(writeBuf, int(int len));  
  7.     MOCK_METHOD0(closeSock, void());  
  8. };  


其中,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 plain  copy
 
  1. ON_CALL(subObj, readBuf(1000)).WillByDefault(Return(blen));  


什麼意思呢?再用剛纔的解釋方法:

 

ON_CALL(#1, #2(#3)).WillByDefault(Return(#4));

#1表示mock對象。就像我上面所說,對CSubscriber我定義了一個Mock類,那麼就必須生成相應的mock對象,例如:

 

[cpp]  view plain  copy
 
  1. MockCSubscriber subObj(5);  


#2表示想定義的那個方法名稱。上例中我想定義readBuf這個方法的返回值。

 

#3表示readBuf方法的參數。這裏的1000表示,只有調用CSubscriber::readBuf同時傳遞參數爲1000時,纔會用到ON_CALL的定義。

#4表示調用CSubscriber::readBuf同時傳遞參數爲1000時,返回blen這個變量的值。

 

c)、若是還但願mock方法有固定的被調用方式

這裏用到宏EXPECT_CALL,看個例子:

 

[cpp]  view plain  copy
 
  1. EXPECT_CALL(subObj, readBuf(1000)).Times(1);  


很類似吧?最後的Times表示,只但願readBuf在傳遞參數爲1000時,被調用且僅被調用一次。

 

 

其實這些宏有很複雜的用法的,例如:

 

[cpp]  view plain  copy
 
  1. EXPECT_CALL(subObj, readBuf(1000))  
  2.     .Times(5)  
  3.     .WillOnce(Return(100))  
  4.     .WillOnce(Return(150))  
  5.     .WillRepeatedly(Return(200));  


表示,readBuf但願被調用五次,第一次返回100,第二次返回150,後三次返回200。若是不知足,會報錯。

 

 

d)、實際的調用測試

其實調用跟上篇googletest文章裏的測試是一致的,我這裏只列下上文的完整用例代碼(不包括被測試類的實現代碼):

 

[cpp]  view plain  copy
 
  1. #include "gtest/gtest.h"  
  2. #include "gmock/gmock.h"  
  3. #include "CSubscriber.h"  
  4. #include "CPublisher.h"  
  5. #include "CSubEventHandler.h"  
  6. #include "CPubEventHandler.h"  
  7.   
  8. using ::testing::AtLeast;  
  9. using testing::Return;  
  10.   
  11.   
  12. class MockCSubscriber : public CSubscriber  
  13. {  
  14. public:  
  15.     MockCSubscriber(int fd):CSubscriber(fd){}  
  16.     MOCK_METHOD1(readBuf, int(int len));  
  17.     MOCK_METHOD1(writeBuf, int(int len));  
  18.     MOCK_METHOD0(closeSock, void());  
  19. };  
  20.   
  21. class MockCPublisher : public CPublisher  
  22. {  
  23. public:  
  24.     MockCPublisher(int fd):CPublisher(fd){}  
  25.     MOCK_METHOD1(readBuf, int(int len));  
  26.     MOCK_METHOD1(writeBuf, int(int len));  
  27.     MOCK_METHOD0(closeSock, void());  
  28. };  
  29.   
  30.   
  31. TEST(subpubHandler, sub1pub1) {  
  32.     MockCSubscriber subObj(5);  
  33.     MockCPublisher pubObj(5);  
  34.   
  35.     subObj.m_iRecvBufLen = 1000;  
  36.     pubObj.m_iRecvBufLen = 1000;  
  37.   
  38.     char* pSubscribeBuf = "GET / HTTP/1.1\r\nobject: /tt/aa\r\ntime: 112\r\n\r\n";  
  39.     char* pMessageBuf = "GET / HTTP/1.1\r\nobject: /tt/aa\r\ntime: 112\r\nmessage: tttt\r\n\r\n";  
  40.     subObj.m_pRecvBuf = pSubscribeBuf;  
  41.     int blen = strlen(pSubscribeBuf);  
  42.     subObj.m_iRecvPos = blen;  
  43.   
  44.     pubObj.m_pRecvBuf = pMessageBuf;  
  45.     int mlen = strlen(pMessageBuf);  
  46.     pubObj.m_iRecvPos = mlen;  
  47.   
  48.   
  49.     ON_CALL(subObj, readBuf(1000)).WillByDefault(Return(blen));  
  50.     ON_CALL(subObj, writeBuf(CEventHandler::InternalError.size())).WillByDefault(Return(0));  
  51.   
  52.     CSubEventHandler subHandler(NULL);  
  53.     CPubEventHandler pubHandler(NULL);  
  54.   
  55.     CHashTable ht1(100);  
  56.     CHashTable ht2(100);  
  57.     subHandler.initial(100, &ht1, &ht2);  
  58.     pubHandler.initial(100, &ht1, &ht2);  
  59.   
  60.     EXPECT_CALL(subObj, readBuf(1000)).Times(1);  
  61.     //EXPECT_CALL(subObj, closeSock()).Times(1);  
  62.     EXPECT_CALL(subObj, writeBuf(4)).Times(1);  
  63.   
  64.     EXPECT_TRUE(subHandler.handleRead(&subObj));  
  65.   
  66.     ON_CALL(pubObj, readBuf(1000)).WillByDefault(Return(mlen));  
  67.     ON_CALL(pubObj, writeBuf(4)).WillByDefault(Return(0));  
  68.   
  69.     EXPECT_CALL(pubObj, readBuf(1000)).Times(1);  
  70.     EXPECT_CALL(pubObj, closeSock()).Times(1);  
  71.     EXPECT_CALL(pubObj, writeBuf(CEventHandler::Success.size())).Times(1);  
  72.   
  73.     EXPECT_TRUE(pubHandler.handleRead(&pubObj));  
  74. }  


CSubscriber的頭文件:

 

 

[cpp]  view plain  copy
 
  1. class CSubscriber : public CBaseConnection, public CHashElement  
  2. {  
  3. public:  
  4.     CSubscriber(int fd);  
  5.       
  6.     virtual ~CSubscriber();  
  7.   
  8.     bool initial();  
  9.   
  10.     bool reset();  
  11.   
  12.     //function return:  
  13.     //0: means complete read, all elements parsed OK  
  14.     //1: means it need recv more buf, not it's not complete  
  15.     //-1: means the packet is not valid.  
  16.     //-2: means connection wrong.  
  17.     int readPacket();  
  18.   
  19.     //max send buf length  
  20.     static int m_iSendBufLen;  
  21.   
  22.     //max recv buf length  
  23.     static int m_iRecvBufLen;  
  24.   
  25. private:  
  26.     /*request format: 
  27.      * GET /objectname?ts=xxx HTTP/1.x\r\n\r\n*/  
  28.     bool parsePacket();  
  29. };  



 

e)、main函數的寫法

與gtest相同,惟一的區別是初始化參數,以下:

 

[cpp]  view plain  copy
 
  1. #include <gmock/gmock.h>  
  2.   
  3. int main(int argc, char** argv) {  
  4.     testing::InitGoogleMock(&argc, argv);  
  5.     //testing::InitGoogleTest(&argc, argv);  
  6.   
  7.     // Runs all tests using Google Test.  
  8.     return RUN_ALL_TESTS();  
  9. }  

如此,就能夠完整的使用googletest/googlemock作C++工程的單元測試了,確實很簡單好用。

相關文章
相關標籤/搜索