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,理由与上篇相同。

验证这个包有没有问题,依然可以执行如下命令:

  1. cd make
  2. make
  3. ./gmock_test

如果你看到类似下文的输出屏幕,证明你的机器运行gmock没有问题。

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

这时还没有编译出我们要的libgmock.a呢。继续在gmock解包目录下执行:

  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了。

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类如下:

  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。看例子:

  1. ON_CALL(subObj, readBuf(1000)).WillByDefault(Return(blen));

什么意思呢?再用刚才的解释方法:

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

#1表示mock对象。就像我上面所说,对CSubscriber我定义了一个Mock类,那么就必须生成相应的mock对象,例如:

  1. MockCSubscriber subObj(5);

#2表示想定义的那个方法名称。上例中我想定义readBuf这个方法的返回值。

#3表示readBuf方法的参数。这里的1000表示,只有调用CSubscriber::readBuf同时传递参数为1000时,才会用到ON_CALL的定义。

#4表示调用CSubscriber::readBuf同时传递参数为1000时,返回blen这个变量的值。

c)、如果还希望mock方法有固定的被调用方式

这里用到宏EXPECT_CALL,看个例子:

  1. EXPECT_CALL(subObj, readBuf(1000)).Times(1);

很相似吧?最后的Times表示,只希望readBuf在传递参数为1000时,被调用且仅被调用一次。

其实这些宏有很复杂的用法的,例如:

  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文章里的测试是一致的,我这里只列下上文的完整用例代码(不包括被测试类的实现代码):

  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. using ::testing::AtLeast;
  8. using testing::Return;
  9. class MockCSubscriber : public CSubscriber
  10. {
  11. public:
  12. MockCSubscriber(int fd):CSubscriber(fd){}
  13. MOCK_METHOD1(readBuf, int(int len));
  14. MOCK_METHOD1(writeBuf, int(int len));
  15. MOCK_METHOD0(closeSock, void());
  16. };
  17. class MockCPublisher : public CPublisher
  18. {
  19. public:
  20. MockCPublisher(int fd):CPublisher(fd){}
  21. MOCK_METHOD1(readBuf, int(int len));
  22. MOCK_METHOD1(writeBuf, int(int len));
  23. MOCK_METHOD0(closeSock, void());
  24. };
  25. TEST(subpubHandler, sub1pub1) {
  26. MockCSubscriber subObj(5);
  27. MockCPublisher pubObj(5);
  28. subObj.m_iRecvBufLen = 1000;
  29. pubObj.m_iRecvBufLen = 1000;
  30. char* pSubscribeBuf = "GET / HTTP/1.1\r\nobject: /tt/aa\r\ntime: 112\r\n\r\n";
  31. char* pMessageBuf = "GET / HTTP/1.1\r\nobject: /tt/aa\r\ntime: 112\r\nmessage: tttt\r\n\r\n";
  32. subObj.m_pRecvBuf = pSubscribeBuf;
  33. int blen = strlen(pSubscribeBuf);
  34. subObj.m_iRecvPos = blen;
  35. pubObj.m_pRecvBuf = pMessageBuf;
  36. int mlen = strlen(pMessageBuf);
  37. pubObj.m_iRecvPos = mlen;
  38. ON_CALL(subObj, readBuf(1000)).WillByDefault(Return(blen));
  39. ON_CALL(subObj, writeBuf(CEventHandler::InternalError.size())).WillByDefault(Return(0));
  40. CSubEventHandler subHandler(NULL);
  41. CPubEventHandler pubHandler(NULL);
  42. CHashTable ht1(100);
  43. CHashTable ht2(100);
  44. subHandler.initial(100, &ht1, &ht2);
  45. pubHandler.initial(100, &ht1, &ht2);
  46. EXPECT_CALL(subObj, readBuf(1000)).Times(1);
  47. //EXPECT_CALL(subObj, closeSock()).Times(1);
  48. EXPECT_CALL(subObj, writeBuf(4)).Times(1);
  49. EXPECT_TRUE(subHandler.handleRead(&subObj));
  50. ON_CALL(pubObj, readBuf(1000)).WillByDefault(Return(mlen));
  51. ON_CALL(pubObj, writeBuf(4)).WillByDefault(Return(0));
  52. EXPECT_CALL(pubObj, readBuf(1000)).Times(1);
  53. EXPECT_CALL(pubObj, closeSock()).Times(1);
  54. EXPECT_CALL(pubObj, writeBuf(CEventHandler::Success.size())).Times(1);
  55. EXPECT_TRUE(pubHandler.handleRead(&pubObj));
  56. }

CSubscriber的头文件:

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

e)、main函数的写法

与gtest相同,唯一的区别是初始化参数,如下:

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

如此,就可以完整的使用googletest/googlemock做C++工程的单元测试了,确实很简单好用。

用google mock模拟C++对象的更多相关文章

  1. google mock C++单元测试框架

    转:google mock C++单元测试框架 2012-03-12 09:33:59 http://blog.chinaunix.net/uid-25748718-id-3129590.html G ...

  2. Python 的mock模拟测试介绍

    如何不靠耐心测试 可能我们正在写一个社交软件并且想测试一下"发布到Facebook的功能",但是我们不希望每次运行测试集的时候都发布到Facebook上. Python的unitt ...

  3. springboot2.0入门(四)----mock模拟测试+单元测试

    一.本节主要记录模拟测试.单元测试: 二.mock 测试 1.1什么是Mock? 在面向对象程序设计中,模拟对象(英语:mock object,也译作模仿对象)是以可控的方式模拟真实对象行为的假的对象 ...

  4. dotnet 如何在 Mock 模拟 Func 判断调用次数

    在 dotnet 程序有很好用的 Mock 框架,可以用来模拟各种接口和抽象类,可以用来测试某个注入接口的被调用次数和被调用时传入参数.本文告诉大家如何在 Mock 里面模拟一个 Func 同时模拟返 ...

  5. PowerMockito 同时mock多个对象

    有时候,需要测试的方法内有collections结构,就需要同时mock多个对象 被测方法: public class EmployeeService { public List<Integer ...

  6. 模拟Select-Options对象实现多项数据输入功能

       模拟Select-Options对象实现多项数据输入功能 Select-Options对象可以同时输入多项值并将所输入数据存入内表以供程序使用,不过Select-Options的功能有一定的局限 ...

  7. [转载] google mock cookbook

    原文: https://code.google.com/p/googlemock/wiki/CookBook Creating Mock Classes Mocking Private or Prot ...

  8. [转载] google mock CheatSheet

    原文: https://code.google.com/p/googlemock/wiki/CheatSheet Defining a Mock Class Mocking a Normal Clas ...

  9. js模拟Map对象,实现key---value

    js模拟Map对象,实现key---value 根据java中map的属性,实现key----value保存 function Map() { var struct = function (key, ...

随机推荐

  1. 爬虫基础库之requests

    requests Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作, ...

  2. Longest Palindromic Substring (最长回文字符串)——两种方法还没看,仍需认真看看

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  3. Java基础:类加载机制

    之前的<java基础:内存模型>当中,我们大体了解了在java当中,不同类型的信息,都存放于java当中哪个部位当中,那么有了对于堆.栈.方法区.的基本理解以后,今天我们来好好剖析一下,j ...

  4. Kbengine cocos2djs 地图问题

    KBEngine.addSpaceGeometryMapping(self.spaceID, None, resPath) 问下这个resPath加载的文件在哪里,后端愣是没找到,前端倒是看到了,还是 ...

  5. 合并区间(LintCode)

    合并区间 给出若干闭合区间,合并所有重叠的部分. 样例 给出的区间列表 => 合并后的区间列表: [ [ [1, 3], [1, 6], [2, 6], => [8, 10], [8, 1 ...

  6. 二进制求和(LintCode)

    二进制求和 给定两个二进制字符串,返回他们的和(用二进制表示). 样例 a = 11 b = 1 返回 100 细节出了好多问题,提交了好多次... public class Solution { / ...

  7. Loj#6434「PKUSC2018」主斗地(搜索)

    题面 Loj 题解 细节比较多的搜索题. 首先现将牌型暴力枚举出来,大概是\(3^{16}\)吧. 然后再看能打什么,简化后无非就三种决策:单牌,\(3+x\)和\(4+x\). 枚举网友打了几张\( ...

  8. 3000大洋,自己配置i5主机

    自用的HP笔记本已经“七年之痒”了,所以在2016年新年之前,换了台新电脑.主要用来娱乐(电影),但是也要能够满足工作学习(本人从事IT行业)的需要. 遵循本人一贯的高冷装X的习惯,决定自己买配件攒机 ...

  9. Linux基础系列-Day5

    网络管理 ifconfig网络管理工具 ifconfig依赖于命令中使用一些选项属性,不仅可以被用来简单地获取网络接口配置信息,还可以修改这些配置,但是通过ifconfig修改的通常为临时配置,即系统 ...

  10. BZOJ 1934 [Shoi2007]Vote 善意的投票(最小割)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1934 [题目大意] 每个人对于投票都有自己原来的观点:1或者0, 他可以违背自己原来的 ...