用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++工程的单元测试了,确实很简单好用。

相关文章
相关标签/搜索