我是靠谱客的博主 长情荔枝,最近开发中收集的这篇文章主要介绍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做单元测试。3、如何运行单元测试;4、gmock的工作原理。


1、如何获取、编译google mock

gmock的当前版本与gtest一样,是1.6.0。可以从这个网址获取:http://code.google.com/p/googlemock/downloads/list。

下载到压缩包解压后,下面我们开始编译出静态库文件(必须得自己编译出),以在我们自己的单元测试工程中使用。

与gtest相同,我们执行完./configure; make后,不能执行make install,理由与上篇相同。

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

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

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

[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解包目录下执行:

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


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 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.1rnobject: /tt/aarntime: 112rnrn";  
  39.     char* pMessageBuf = "GET / HTTP/1.1rnobject: /tt/aarntime: 112rnmessage: ttttrnrn";  
  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.xrnrn*/  
  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++工程的单元测试了,确实很简单好用。

Google Test


1. 自定义错误输出:

<span class="pln">ASSERT_EQ</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="pln">x</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">size</span><span class="pun" style="color: rgb(102, 102, 0);">(),</span><span class="pln"> y</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">size</span><span class="pun" style="color: rgb(102, 102, 0);">())</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);"><<</span><span class="pln"> </span><span class="str" style="color: rgb(0, 136, 0);">"Vectors x and y are of unequal length"</span><span class="pun" style="color: rgb(102, 102, 0);">;</span><span class="pln">
</span><span class="kwd" style="color: rgb(0, 0, 136);">for</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="kwd" style="color: rgb(0, 0, 136);">int</span><span class="pln"> i </span><span class="pun" style="color: rgb(102, 102, 0);">=</span><span class="pln"> </span><span class="lit" style="color: rgb(0, 102, 102);">0</span><span class="pun" style="color: rgb(102, 102, 0);">;</span><span class="pln"> i </span><span class="pun" style="color: rgb(102, 102, 0);"><</span><span class="pln"> x</span><span class="pun" style="color: rgb(102, 102, 0);">.</span><span class="pln">size</span><span class="pun" style="color: rgb(102, 102, 0);">();</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);">++</span><span class="pln">i</span><span class="pun" style="color: rgb(102, 102, 0);">)</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);">{</span><span class="pln">
  EXPECT_EQ</span><span class="pun" style="color: rgb(102, 102, 0);">(</span><span class="pln">x</span><span class="pun" style="color: rgb(102, 102, 0);">[</span><span class="pln">i</span><span class="pun" style="color: rgb(102, 102, 0);">],</span><span class="pln"> y</span><span class="pun" style="color: rgb(102, 102, 0);">[</span><span class="pln">i</span><span class="pun" style="color: rgb(102, 102, 0);">])</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);"><<</span><span class="pln"> </span><span class="str" style="color: rgb(0, 136, 0);">"Vectors x and y differ at index "</span><span class="pln"> </span><span class="pun" style="color: rgb(102, 102, 0);"><<</span><span class="pln"> i</span><span class="pun" style="color: rgb(102, 102, 0);">;</span><span class="pln">
</span><span class="pun" style="color: rgb(102, 102, 0);">}</span>

2. ASSERT_* 与EXPECT_*系列的区别:

ASSERT_TRUE(condition)ASSERT_FALSE(condition)ASSERT失败导致当前case终止
EXPECT_TRUE(condition)EXPECT_FALSE(condition)EXPECT失败,打印错误,并继续执行当前case
在GTest中,二者的称呼为:Fatal assertion, Nonfatal assertion

什么时候用ASSERT,什么时候用EXPECT呢?一般性的原则是,当你希望程序出错后继续运行,以便让代码暴露出更多的潜在错误的时候,使用EXPECT。当你认为某处出错后继续运行没有意义的时候,使用ASSERT。例如,某个对象如果为NULL,后面对它的引用会导致null pointer error,那么最好还是用ASSERT_NE(NULL, obj)。




3. 比较运算符

ASSERT_*, EXPECT_*都有:

ASSERT_EQ, ASSERT_NE,ASSERT_LT,ASSERT_GT,ASSERT_LE,ASSERT_GE

EXPECT_EQ, EXPECT_NE,EXPECT_LT,EXPECT_GT,EXPECT_LE,EXPECT_GE

另外,GTEST还支持STRING的比较。相关函数有(以ASSERT为例)

ASSERT_STREQ,ASSERT_STRNE,ASSERT_STRCASEEQ,ASSERT_STRCASENE


4. TEST与TEST_F之间的区别

TEST_F比TEST强一些的地方在于TEST_F实际上会生成一个新类,该类有SetUp和TearDown函数用于建立和销毁数据结构。

同一个TestCase文件中不能混合使用TEST与TEST_F。

For each test defined with TEST_F(), Google Test will:

  1. Create a fresh test fixture at runtime
  2. Immediately initialize it via SetUp() ,
  3. Run the test
  4. Clean up by calling TearDown()
  5. Delete the test fixture. Note that different tests in the same test case have different test fixture objects, and Google Test always deletes a test fixture before it creates the next one. Google Test does not reuse the same test fixture for multiple tests. Any changes one test makes to the fixture do not affect other tests.


Google Mock

GoogleMock是个很强大的东西,测试一个模块的时候,可能涉及到和其他模块交互,可以将模块之间的接口mock起来,模拟交互过程。


1. Makefile里面需要加入 -lgmock才能正常连接

[cpp]  view plain copy
  1. AM_LDFLAGS=-lpthread -lc -lm -lrt -lgtest -lgmock   


2. 可以手工生成Mock类,也可以使用脚本生成

手工:


[cpp]  view plain copy
  1. class ObProject: public ObSingleChildPhyOperator  
  2. {     
  3.    public:  
  4.         ObProject();  
  5.         virtual ~ObProject();  
  6.         void reset(){};  
  7.   
  8.   
  9.         int add_output_column(const ObSqlExpression& expr);  
  10.         virtual int open();  
  11.         virtual int close();  
  12.         virtual int get_next_row(const common::ObRow *&row);  
  13.         virtual int64_t to_string(char* buf, const int64_t buf_len) const;  
  14.         ....  
  15.  };  
  16.   
  17. class MockObProject : public ObProject  
  18.   
  19. {  
  20.   public:  
  21.   MOCK_METHOD0(open, int());  
  22.   MOCK_METHOD0(close, int());  
  23.   MOCK_METHOD1(add_output_column, int(const ObSqlExpression &expr));  
  24. };  

脚本:

需要mock ob_ms_tablet_location_proxy.h中的ObMergerLocationCacheProxy类,方法如下:

[plain]  view plain copy
  1. gmock_installed_dir/scripts/generator/gmock_gen.py ob_ms_tablet_location_proxy.h ObMergerLocationCacheProxy  

3. 一个类中,只有virtual的member funciton能被mock(试验得到的结论),调用被mock的member function,function行为变成mocked behavior,调用类中没有被mock的member function,function行为与原类相同,不被mock改变。

ps,写了一个简单类,不是virtual居然也能被mock,奇怪。。。。在一个复杂类中,必须是virtual的才能被mock。 这些是实验得到的结论。

从理论上分析,应该必须是virtual才可以。所以,确定哪些函数要被mock,然后在头文件中将其virtual化。不然可别说我没有预先告诉你哦;)


4. 一个被Mock的函数,如果没有在EXPECT_CALL中指定expected behavior,系统将会为其指派默认行为(什么都不做,返回0),并且在屏幕上打印WARNING:

GMOCK WARNING:
Uninteresting mock function call - returning default value.
    Function call: get_next_row(@0x7fff51a6b888 0x30c51529e0)
          Returns: 0
Stack trace:


Content

  • Matcher(匹配器)
  • 基数(Cardinalities)
  • 行为(Actions)
  • 序列(Sequences)
  • Google Mock 入门
  • 概述
  • Google Mock使用
  • Mock实践
  • Google Mock Cookbook
  • 什么是Mock?
  • Google Mock概述
  • 参考文档
  • 最简单的例子
  • 典型的流程
  • 自定义方法/成员函数的期望行为
  • 我改过的例子
  • 现实中的例子
  • Mock protected、private方法
  • Mock 模版类(Template Class)
  • Nice Mocks 和 Strict Mocks
Google Mock 入门概述什么是Mock?

Mock,更确切地说应该是Mock Object。它究竟是什么?它有什么作用?在这里,我也只能先说说我的理解。 比如当我们在单元测试、模块的接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(那名开发同学比较懒,呵呵),这时我们就可以定义了Mock对象来模拟那些类的行为。
说得更直白一些,就是自己实现一个假的依赖类,对这个类的方法你想要什么行为就可以有什么行为,你想让这个方法返回什么结果就可以返回怎么样的结果。
但这时很多同学往往会提出一个问题:"那既然是我自己实现一个假的依赖类",那和那些市面上的Mock框架有什么关系啊?
这个其实是这样的,这些个Mock框架可以帮助你比较方便、比较轻松地实现这些个假的依赖类。毕竟,如果你实现这么一个假的依赖类的时间花费过场的话,那我还不如等待那位懒惰的同学吧。

Google Mock概述

Google Mock(简称gmock)是Google在2008年推出的一套针对C++的Mock框架,它灵感取自于jMock、EasyMock、harcreat。它提供了以下这些特性:

  • 轻松地创建mock类
  • 支持丰富的匹配器(Matcher)和行为(Action)
  • 支持有序、无序、部分有序的期望行为的定义
  • 多平台的支持
参考文档
  • 新人手册
  • Cheat Sheet
  • Cheat Sheet中文翻译
  • Cookbook
Google Mock使用最简单的例子

我比较喜欢举例来说明这些个、那些个玩意,因此我们先来看看Google Mock就简单的用法和作用。

  • 首先,那个懒惰的同学已经定义好了这么一个接口(万幸,他至少把接口定义好了):

FooInterface.h

  1. #ifndef FOOINTERFACE_H_
  2. #define FOOINTERFACE_H_

  3. #include <string>

  4. namespace seamless {

  5. class FooInterface {
  6. public:
  7.         virtual ~FooInterface() {}

  8. public:
  9.         virtual std::string getArbitraryString() = 0;
  10. };

  11. }  // namespace seamless

  12. #endif // FOOINTERFACE_H_
这里需要注意几点:

  • FooInterface的析构函数~FooInterface()必须是virtual的
  • 在第13行,我们得把getArbitraryString定义为纯虚函数。其实getArbitraryString()也不一定得是纯虚函数,这点我们后面会提到.

现在我们用Google Mock来定义Mock类 FooMock.h

  1. #ifndef MOCKFOO_H_
  2. #define MOCKFOO_H_

  3. #include <gmock/gmock.h>
  4. #include <string>
  5. #include "FooInterface.h"

  6. namespace seamless {

  7. class MockFoo: public FooInterface {
  8. public:
  9.         MOCK_METHOD0(getArbitraryString, std::string());
  10. };

  11. }  // namespace seamless

  12. #endif // MOCKFOO_H_
我们稍微来解释一下这个Mock类的定义:

  • 第10行我们的MockFoo类继承懒同学的FooInterface
  • 第22行我们定义使用gmock中的一个宏(Macro)MOCK_METHOD0来定义MockFoo中的getArbitraryString。Google Mock是需要你根据不同的形参个数来使用不同的Mock Method,我这里getArbitraryString没有函数,就是MOCK_METHOD0了,同理,如果是一个形参,就是MOCK_METHOD1了,以此往下。

FooMain.cc

  1. #include <cstdlib>
  2. #include <gmock/gmock.h>
  3. #include <gtest/gtest.h>
  4. #include <iostream>
  5. #include <string>

  6. #include "MockFoo.h"

  7. using namespace seamless;
  8. using namespace std;

  9. using ::testing::Return;

  10. int main(int argc, char** argv) {
  11.         ::testing::InitGoogleMock(&argc, argv);

  12.         string value = "Hello World!";
  13.         MockFoo mockFoo;
  14.         EXPECT_CALL(mockFoo, getArbitraryString()).Times(1).
  15.                 WillOnce(Return(value));
  16.         string returnValue = mockFoo.getArbitraryString();
  17.         cout << "Returned Value: " << returnValue << endl;

  18.         return EXIT_SUCCESS;
  19. }

最后我们运行编译,得到的结果如下:

Returned Value: Hello World!

在这里:

  • 第15行,初始化一个Google Mock
  • 第18行,声明一个MockFoo的对象:mockFoo
  • 第19行,是为MockFoo的getArbitraryString()方法定义一个期望行为,其中Times(1)的意思是运行一次,WillOnce(Return(value))的意思是第一次运行时把value作为getArbitraryString()方法的返回值。

这就是我们最简单的使用Google Mock的例子了,使用起来的确比较简便吧。

典型的流程

通过上述的例子,已经可以看出使用Mock类的一般流程如下:

  • 引入你要用到的Google Mock名称. 除宏或其它特别提到的之外所有Google Mock名称都位于*testing*命名空间之下.
  • 建立模拟对象(Mock Objects).
  • 可选的,设置模拟对象的默认动作.
  • 在模拟对象上设置你的预期(它们怎样被调用,应该怎样回应?).
自定义方法/成员函数的期望行为

从上述的例子中可以看出,当我们针对懒同学的接口定义好了Mock类后,在单元测试/主程序中使用这个Mock类中的方法时最关键的就是对期望行为的定义。
对方法期望行为的定义的语法格式如下:

  1. EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
  2.     .With(multi_argument_matcher)
  3.     .Times(cardinality)
  4.     .InSequence(sequences)
  5.     .After(expectations)
  6.     .WillOnce(action)
  7.     .WillRepeatedly(action)
  8.     .RetiresOnSaturation();
解释一下这些参数(虽然很多我也没弄明白):

  • 第1行的mock_object就是你的Mock类的对象
  • 第1行的method(matcher1, matcher2, …)中的method就是你Mock类中的某个方法名,比如上述的getArbitraryString;而matcher(匹配器)的意思是定义方法参数的类型,我们待会详细介绍。
  • 第3行的Times(cardinality)的意思是之前定义的method运行几次。至于cardinality的定义,我也会在后面详细介绍。
  • 第4行的InSequence(sequences)的意思是定义这个方法被执行顺序(优先级),我会再后面举例说明。
  • 第6行WillOnce(action)是定义一次调用时所产生的行为,比如定义该方法返回怎么样的值等等。
  • 第7行WillRepeatedly(action)的意思是缺省/重复行为。

我稍微先举个例子来说明一下,后面有针对更为详细的说明:

  1. EXPECT_CALL(mockTurtle, getX()).Times(testing::AtLeast(5)).
  2.                 WillOnce(testing::Return(100)).WillOnce(testing::Return(150)).
  3.                 WillRepeatedly(testing::Return(200))
这个期望行为的定义的意思是:

  • 调用mockTurtle的getX()方法
  • 这个方法会至少调用5次
  • 第一次被调用时返回100
  • 第2次被调用时返回150
  • 从第3次被调用开始每次都返回200
Matcher(匹配器)

Matcher用于定义Mock类中的方法的形参的值(当然,如果你的方法不需要形参时,可以保持match为空。),它有以下几种类型:(更详细的介绍可以参见Google Mock Wiki上的Matcher介绍)
通配符

_ 可以代表任意类型
A() or An() 可以是type类型的任意值
这里的 _和*A*包括下面的那个匹配符都在Google Mock的* ::testing*这个命名空间下,大家要用时需要先引入那个命名空间

一般比较

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

浮点数的比较

DoubleEq(a_double) 形参是一个double类型,比如值近似于a_double,两个NaN是不相等的
FloatEq(a_float) 同上,只不过类型是float
NanSensitiveDoubleEq(a_double) 形参是一个double类型,比如值近似于a_double,两个NaN是相等的,这个是用户所希望的方式
NanSensitiveFloatEq(a_float) 同上,只不过形参是float

字符串匹配
这里的字符串即可以是C风格的字符串,也可以是C++风格的。

ContainsRegex(string) 形参匹配给定的正则表达式
EndsWith(suffix) 形参以suffix截尾
HasSubstr(string) 形参有string这个子串
MatchesRegex(string) 从第一个字符到最后一个字符都完全匹配给定的正则表达式.
StartsWith(prefix) 形参以prefix开始
StrCaseEq(string) 参数等于string,并且忽略大小写
StrCaseNe(string) 参数不是string,并且忽略大小写
StrEq(string) 参数等于string
StrNe(string) 参数不等于string

容器的匹配
很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(container)来比较。但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:

Contains(e) 在method的形参中,只要有其中一个元素等于e
Each(e) 参数各个元素都等于e
ElementsAre(e0, e1, …, en) 形参有n+1的元素,并且挨个匹配
ElementsAreArray(array) 或者ElementsAreArray(array, count) 和ElementsAre()类似,除了预期值/匹配器来源于一个C风格数组
ContainerEq(container) 类型Eq(container),就是输出结果有点不一样,这里输出结果会带上哪些个元素不被包含在另一个容器中
Pointwise(m, container)  

上述的一些匹配器都比较简单,我就随便打包举几最简单的例子演示一下吧: 我稍微修改一下之前的Foo.hMockFoo.h, MockFoo.h 增加了2个方法

  1. #ifndef MOCKFOO_H_
  2. #define MOCKFOO_H_

  3. #include <gmock/gmock.h>
  4. #include <string>
  5. #include <vector>
  6. #include "FooInterface.h"

  7. namespace seamless {

  8. class MockFoo: public FooInterface {
  9. public:
  10.         MOCK_METHOD0(getArbitraryString, std::string());
  11.         MOCK_METHOD1(setValue, void(std::string& value));
  12.         MOCK_METHOD2(setDoubleValues, void(int x, int y));
  13. };

  14. }  // namespace seamless

  15. #endif // MOCKFOO_H_

FooMain.h

  1. #include <cstdlib>
  2. #include <gmock/gmock.h>
  3. #include <iostream>
  4. #include <string>

  5. #include "MockFoo.h"

  6. using namespace seamless;
  7. using namespace std;

  8. using ::testing::Assign;
  9. using ::testing::Eq;
  10. using ::testing::Ge;
  11. using ::testing::Return;

  12. int main(int argc, char** argv) {
  13.         ::testing::InitGoogleMock(&argc, argv);

  14.         string value = "Hello World!";
  15.         MockFoo mockFoo;

  16.         EXPECT_CALL(mockFoo, setValue(testing::_));
  17.         mockFoo.setValue(value);

  18.         // 这里我故意犯错
  19.         EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)));
  20.         mockFoo.setDoubleValues(1, 0);

  21.         return EXIT_SUCCESS;
  22. }
  • 第22行,让setValue的形参可以传入任意参数
  • 另外,我在第26~27行故意犯了个错(为了说明上述这些匹配器的作用),我之前明明让setDoubleValues第二个参数得大于等于1,但我实际传入时却传入一个0。这时程序运行时就报错了:
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

上述的那些匹配器都比较简单,下面我们来看看那些比较复杂的匹配吧。
成员匹配器

Field(&class::field, m) argument.field (或 argument->field, 当argument是一个指针时)与匹配器m匹配, 这里的argument是一个class类的实例.
Key(e) 形参(argument)比较是一个类似map这样的容器,然后argument.first的值等于e
Pair(m1, m2) 形参(argument)必须是一个pair,并且argument.first等于m1,argument.second等于m2.
Property(&class::property, m) argument.property()(或argument->property(),当argument是一个指针时)与匹配器m匹配, 这里的argument是一个class类的实例.

还是举例说明一下:

  1. TEST(TestField, Simple) {
  2.         MockFoo mockFoo;
  3.         Bar bar;
  4.         EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
  5.         mockFoo.get(bar);
  6. }

  7. int main(int argc, char** argv) {
  8.         ::testing::InitGoogleMock(&argc, argv);
  9.         return RUN_ALL_TESTS();
  10. }
这里我们使用 Google Test来写个测试用例,这样看得比较清楚。

  • 第5行,我们定义了一个Field(&Bar::num, Ge(0)),以说明Bar的成员变量num必须大于等于0。

上面这个是正确的例子,我们为了说明Field的作用,传入一个bar.num = -1试试。

  1. TEST(TestField, Simple) {
  2.         MockFoo mockFoo;
  3.         Bar bar;
  4.         bar.num = -1;
  5.         EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
  6.         mockFoo.get(bar);
  7. }
运行是出错了:

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

匹配函数或函数对象的返回值

ResultOf(f, m) f(argument) 与匹配器m匹配, 这里的f是一个函数或函数对象.

指针匹配器

Pointee(m) argument (不论是智能指针还是原始指针) 指向的值与匹配器m匹配.

复合匹配器

AllOf(m1, m2, …, mn) argument 匹配所有的匹配器m1到mn
AnyOf(m1, m2, …, mn) argument 至少匹配m1到mn中的一个
Not(m) argument 不与匹配器m匹配
  1. EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));
  • 传入的参数必须 >5 并且 <= 10
  1. EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
  • 第一个参数不包含“blah”这个子串
基数(Cardinalities)

基数用于Times()中来指定模拟函数将被调用多少次|

AnyNumber() 函数可以被调用任意次.
AtLeast(n) 预计至少调用n次.
AtMost(n) 预计至多调用n次.
Between(m, n) 预计调用次数在m和n(包括n)之间.
Exactly(n) 或 n 预计精确调用n次. 特别是, 当n为0时,函数应该永远不被调用.
行为(Actions)

Actions(行为)用于指定Mock类的方法所期望模拟的行为:比如返回什么样的值、对引用、指针赋上怎么样个值,等等。 值的返回

Return() 让Mock方法返回一个void结果
Return(value) 返回值value
ReturnNull() 返回一个NULL指针
ReturnRef(variable) 返回variable的引用.
ReturnPointee(ptr) 返回一个指向ptr的指针

另一面的作用(Side Effects)

Assign(&variable, value) 将value分配给variable

使用函数或者函数对象(Functor)作为行为

Invoke(f) 使用模拟函数的参数调用f, 这里的f可以是全局/静态函数或函数对象.
Invoke(object_pointer, &class::method) 使用模拟函数的参数调用object_pointer对象的mothod方法.

复合动作

DoAll(a1, a2, …, an) 每次发动时执行a1到an的所有动作.
IgnoreResult(a) 执行动作a并忽略它的返回值. a不能返回void.

这里我举个例子来解释一下DoAll()的作用,我个人认为这个DoAll()还是挺实用的。例如有一个Mock方法:

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

对于这个方法,我这回需要操作的结果是将name指向value的地址,并且得到方法的返回值。
类似这样的需求,我们就可以这样定义期望过程:

  1. TEST(SimpleTest, F1) {
  2.     std::string* a = new std::string("yes");
  3.     std::string* b = new std::string("hello");
  4.     MockIParameter mockIParameter;
  5.     EXPECT_CALL(mockIParameter, getParamter(testing::_, testing::_)).Times(1).
  6.         WillOnce(testing::DoAll(testing::Assign(&a, b), testing::Return(1)));
  7.     mockIParameter.getParamter(a, b);
  8. }
这时就用上了我们的DoAll()了,它将Assign()和Return()结合起来了。
序列(Sequences)

默认时,对于定义要的期望行为是无序(Unordered)的,即当我定义好了如下的期望行为:

  1.         MockFoo mockFoo;
  2.         EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
  3.         EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
对于这样的期望行为的定义,我何时调用 mockFoo.getValue()或者何时 mockFoo.getSize()都可以的。

但有时候我们需要定义有序的(Ordered)的调用方式,即序列 (Sequences) 指定预期的顺序. 在同一序列里的所有预期调用必须按它们指定的顺序发生; 反之则可以是任意顺序.

  1. using ::testing::Return;
  2. using ::testing::Sequence;

  3. int main(int argc, char **argv) {
  4.         ::testing::InitGoogleMock(&argc, argv);

  5.         Sequence s1, s2;
  6.         MockFoo mockFoo;
  7.         EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2).WillOnce(Return(1));
  8.         EXPECT_CALL(mockFoo, getValue()).InSequence(s1).WillOnce(Return(
  9.                 string("Hello World!")));
  10.         cout << "First:t" << mockFoo.getSize() << endl;
  11.         cout << "Second:t" << mockFoo.getValue() << endl;

  12.         return EXIT_SUCCESS;
  13. }
  • 首先在第8行建立两个序列:s1、s2。
  • 然后在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)说明getSize()的行为优先于s1、s2.
  • 而第12行时,EXPECT_CALL(mockFoo, getValue()).InSequence(s1)说明getValue()的行为在序列s1中。

得到的结果如下:

First: 1
Second: Hello World!

当我尝试一下把mockFoo.getSize()mockFoo.getValue()的调用对调时试试:

  1.         cout << "Second:t" << mockFoo.getValue() << endl;
  2.         cout << "First:t" << mockFoo.getSize() << endl;
得到如下的错误信息:

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

另外,我们还有一个偷懒的方法,就是不要这么傻乎乎地定义这些个Sequence s1, s2的序列,而根据我定义期望行为(EXPECT_CALL)的顺序而自动地识别调用顺序,这种方式可能更为地通用。

  1. using ::testing::InSequence;
  2. using ::testing::Return;

  3. int main(int argc, char **argv) {
  4.         ::testing::InitGoogleMock(&argc, argv);

  5.         InSequence dummy;
  6.         MockFoo mockFoo;
  7.         EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
  8.         EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));

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

  11.         return EXIT_SUCCESS;
  12. }
Mock实践

下面我从我在工作中参与的项目中选取了一个实际的例子来实践Mock。
这个例子的背景是用于搜索引擎的:

  • 引擎接收一个查询的Query,比如http://127.0.0.1/search?q=mp3&retailwholesale=0&isuse_alipay=1
  • 引擎接收到这个Query后,将解析这个Query,将Query的Segment(如q=mp3、retail_wholesale=0放到一个数据结构中)
  • 引擎会调用另外内部模块具体根据这些Segment来处理相应的业务逻辑。

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

我改过的例子

我们先来看看引擎定义好的接口们:
VariantField.h 一个联合体,用于保存Query中的Segment的值

  1. #ifndef VARIANTFIELD_H_
  2. #define VARIANTFIELD_H_

  3. #include <boost/cstdint.hpp>

  4. namespace seamless {

  5. union VariantField
  6. {
  7.     const char * strVal;
  8.     int32_t intVal;
  9. };

  10. }  // namespace mlr_isearch_api

  11. #endif // VARIANTFIELD_H_

IParameterInterface.h 提供一个接口,用于得到Query中的各个Segment的值

  1. #ifndef IPARAMETERINTERFACE_H_
  2. #define IPARAMETERINTERFACE_H_

  3. #include <boost/cstdint.hpp>

  4. #include "VariantField.h"

  5. namespace seamless {

  6. class IParameterInterface {
  7. public:
  8.         virtual ~IParameterInterface() {};

  9. public:
  10.         virtual int32_t getParameter(const char* name,  VariantField*& value) = 0;
  11. };

  12. }  // namespace

  13. #endif // IPARAMETERINTERFACE_H_

IAPIProviderInterface.h 一个统一的外部接口

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_

  3. #include <boost/cstdint.hpp>

  4. #include "IParameterInterface.h"
  5. #include "VariantField.h"

  6. namespace seamless {

  7. class IAPIProviderInterface {
  8. public:
  9.         IAPIProviderInterface() {}
  10.         virtual ~IAPIProviderInterface() {}

  11. public:
  12.         virtual IParameterInterface* getParameterInterface() = 0;
  13. };

  14. }

  15. #endif // IAPIPROVIDERINTERFACE_H_

引擎定义好的接口就以上三个,下面是引擎中的一个模块用于根据Query中的Segment接合业务处理的。Rank.h 头文件

  1. #ifndef RANK_H_
  2. #define RANK_H_

  3. #include "IAPIProviderInterface.h"

  4. namespace seamless {

  5. class Rank {
  6. public:
  7.         virtual ~Rank() {}

  8. public:
  9.         void processQuery(IAPIProviderInterface* iAPIProvider);
  10. };

  11. }  // namespace seamless

  12. #endif // RANK_H_

Rank.cc 实现

  1. #include <cstdlib>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <string>
  5. #include "IAPIProviderInterface.h"
  6. #include "IParameterInterface.h"
  7. #include "VariantField.h"

  8. #include "Rank.h"

  9. using namespace seamless;
  10. using namespace std;

  11. namespace seamless {

  12. void Rank::processQuery(IAPIProviderInterface* iAPIProvider) {
  13.         IParameterInterface* iParameter = iAPIProvider->getParameterInterface();
  14.         if (!iParameter) {
  15.                 cerr << "iParameter is NULL" << endl;
  16.                 return;
  17.         }

  18.         int32_t isRetailWholesale = 0;
  19.         int32_t isUseAlipay = 0;

  20.         VariantField* value = new VariantField;

  21.         iParameter->getParameter("retail_wholesale", value);
  22.         isRetailWholesale = (strcmp(value->strVal, "0")) ? 1 : 0;

  23.         iParameter->getParameter("is_use_alipay", value);
  24.         isUseAlipay = (strcmp(value->strVal, "0")) ? 1 : 0;

  25.         cout << "isRetailWholesale:t" << isRetailWholesale << endl;
  26.         cout << "isUseAlipay:t" << isUseAlipay << endl;

  27.         delete value;
  28.         delete iParameter;
  29. }

  30. }  // namespace seamless
  • 从上面的例子中可以看出,引擎会传入一个IAPIProviderInterface对象,这个对象调用getParameterInterface()方法来得到Query中的Segment。
  • 因此,我们需要Mock的对象也比较清楚了,就是要模拟引擎将Query的Segment传给这个模块。其实就是让=模拟iParameter->getParameter方法:我想让它返回什么样的值就返回什么样的值.

下面我们开始Mock了:
MockIParameterInterface.h 模拟模拟IParameterInterface类

  1. #ifndef MOCKIPARAMETERINTERFACE_H_
  2. #define MOCKIPARAMETERINTERFACE_H_

  3. #include <boost/cstdint.hpp>
  4. #include <gmock/gmock.h>

  5. #include "IParameterInterface.h"
  6. #include "VariantField.h"

  7. namespace seamless {

  8. class MockIParameterInterface: public IParameterInterface {
  9. public:
  10.         MOCK_METHOD2(getParameter, int32_t(const char* name,  VariantField*& value));
  11. };

  12. }  // namespace seamless

  13. #endif // MOCKIPARAMETERINTERFACE_H_

MockIAPIProviderInterface.h 模拟IAPIProviderInterface类

  1. #ifndef MOCKIAPIPROVIDERINTERFACE_H_
  2. #define MOCKIAPIPROVIDERINTERFACE_H_

  3. #include <gmock/gmock.h>

  4. #include "IAPIProviderInterface.h"
  5. #include "IParameterInterface.h"

  6. namespace seamless {

  7. class MockIAPIProviderInterface: public IAPIProviderInterface{
  8. public:
  9.         MOCK_METHOD0(getParameterInterface, IParameterInterface*());
  10. };

  11. }  // namespace seamless

  12. #endif // MOCKIAPIPROVIDERINTERFACE_H_

tester.cc 一个测试程序,试试我们的Mock成果

  1. #include <boost/cstdint.hpp>
  2. #include <boost/shared_ptr.hpp>
  3. #include <cstdlib>
  4. #include <gmock/gmock.h>

  5. #include "MockIAPIProviderInterface.h"
  6. #include "MockIParameterInterface.h"
  7. #include "Rank.h"

  8. using namespace seamless;
  9. using namespace std;

  10. using ::testing::_;
  11. using ::testing::AtLeast;
  12. using ::testing::DoAll;
  13. using ::testing::Return;
  14. using ::testing::SetArgumentPointee;

  15. int main(int argc, char** argv) {
  16.         ::testing::InitGoogleMock(&argc, argv);

  17.         MockIAPIProviderInterface* iAPIProvider = new MockIAPIProviderInterface;
  18.         MockIParameterInterface* iParameter = new MockIParameterInterface;

  19.         EXPECT_CALL(*iAPIProvider, getParameterInterface()).Times(AtLeast(1)).
  20.                 WillRepeatedly(Return(iParameter));

  21.         boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
  22.         retailWholesaleValue->strVal = "0";

  23.         boost::shared_ptr<VariantField> defaultValue(new VariantField);
  24.         defaultValue->strVal = "9";

  25.         EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).
  26.                 WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).
  27.                 WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));

  28.         Rank rank;
  29.         rank.processQuery(iAPIProvider);

  30.         delete iAPIProvider;

  31.         return EXIT_SUCCESS;
  32. }
  • 第26行,定义一个执行顺序,因此在之前的Rank.cc中,是先调用iAPIProvider>getParameterInterface,然后再调用iParameter>getParameter,因此我们在下面会先定义MockIAPIProviderInterface.getParameterInterface的期望行为,然后再是其他的。
  • 第27~28行,定义MockIAPIProviderInterface.getParameterInterface的的行为:程序至少被调用一次(Times(AtLeast(1))),每次调用都返回一个iParameter(即MockIParameterInterface*的对象)。
  • 第30~34行,我自己假设了一些Query的Segment的值。即我想达到的效果是Query类似http://127.0.0.1/search?retailwholesale=0&isuse_alipay=9。
  • 第36~38行,我们定义MockIParameterInterface.getParameter的期望行为:这个方法至少被调用一次;第一次被调用时返回1并将第一个形参指向retailWholesaleValue;后续几次被调用时返回1,并指向defaultValue。
  • 第51行,运行Rank类下的processQuery方法。

看看我们的运行成果:

isRetailWholesale: 0
isUseAlipay: 1

从这个结果验证出我们传入的Query信息是对的,成功Mock!

现实中的例子

就如我之前所说的,上述的那个例子是我改过的,现实项目中哪有这么理想的结构(特别对于那些从来没有Develop for Debug思想的同学)。
因此我们来看看上述这个例子中实际的代码:其实只有IAPIProviderInterface.h不同,它定义了一个模版函数,用于统一各种类型的接口: IAPIProviderInterface.h 真正的IAPIProviderInterface.h,有一个模版函数

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_

  3. #include <boost/cstdint.hpp>
  4. #include <iostream>

  5. #include "IBaseInterface.h"
  6. #include "IParameterInterface.h"
  7. #include "VariantField.h"

  8. namespace seamless {

  9. class IAPIProviderInterface: public IBaseInterface {
  10. public:
  11.         IAPIProviderInterface() {}
  12.         virtual ~IAPIProviderInterface() {}

  13. public:
  14.         virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;

  15.         template<typename InterfaceType>
  16.         InterfaceType* getInterface() {
  17.                 IBaseInterface* pInterface = NULL;
  18.                 if (queryInterface(pInterface)) {
  19.                         std::cerr << "Query Interface failed" << std::endl;
  20.                 }
  21.                 return static_cast<InterfaceType* >(pInterface);
  22.         }
  23. };

  24. }

  25. #endif // IAPIPROVIDERINTERFACE_H_

Rank.cc 既然IAPIProviderInterface.h改了,那Rank.cc中对它的调用其实也不是之前那样的。不过其实也就差一行代码:

  1. //      IParameterInterface* iParameter = iAPIProvider->getParameterInterface();
  2.         IParameterInterface* iParameter = iAPIProvider->getInterface<IParameterInterface>();

因为目前版本(1.5版本)的Google Mock还不支持模版函数,因此我们无法Mock IAPIProviderInterface中的getInterface,那我们现在怎么办?
如果你想做得比较完美的话我暂时也没想出办法,我现在能够想出的办法也只能这样:IAPIProviderInterface.h 修改其中的getInterface,让它根据模版类型,如果是IParameterInterface或者MockIParameterInterface则就返回一个MockIParameterInterface的对象

  1. #ifndef IAPIPROVIDERINTERFACE_H_
  2. #define IAPIPROVIDERINTERFACE_H_

  3. #include <boost/cstdint.hpp>
  4. #include <iostream>

  5. #include "IBaseInterface.h"
  6. #include "IParameterInterface.h"
  7. #include "VariantField.h"

  8. // In order to Mock
  9. #include <boost/shared_ptr.hpp>
  10. #include <gmock/gmock.h>
  11. #include "MockIParameterInterface.h"

  12. namespace seamless {

  13. class IAPIProviderInterface: public IBaseInterface {
  14. public:
  15.         IAPIProviderInterface() {}
  16.         virtual ~IAPIProviderInterface() {}

  17. public:
  18.         virtual int32_t queryInterface(IBaseInterface*& pInterface) = 0;

  19.         template<typename InterfaceType>
  20.         InterfaceType* getInterface() {
  21.                 IBaseInterface* pInterface = NULL;
  22.                 if (queryInterface(pInterface) == 0) {
  23.                         std::cerr << "Query Interface failed" << std::endl;
  24.                 }

  25.                 // In order to Mock
  26.                 if ((typeid(InterfaceType) == typeid(IParameterInterface)) ||
  27.                         (typeid(InterfaceType) == typeid(MockIParameterInterface))) {
  28.                         using namespace ::testing;
  29.                         MockIParameterInterface* iParameter = new MockIParameterInterface;
  30.                         boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
  31.                         retailWholesaleValue->strVal = "0";

  32.                         boost::shared_ptr<VariantField> defaultValue(new VariantField);
  33.                         defaultValue->strVal = "9";

  34.                         EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).
  35.                                 WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).
  36.                                 WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
  37.                         return static_cast<InterfaceType* >(iParameter);
  38.                 }
  39.                 // end of mock

  40.                 return static_cast<InterfaceType* >(pInterface);
  41.         }
  42. };

  43. }

  44. #endif // IAPIPROVIDERINTERFACE_H_
  • 第33~49行,判断传入的模版函数的类型,然后定义相应的行为,最后返回一个MockIParameterInterface对象

tester.cc

  1. int main(int argc, char** argv) {
  2.         ::testing::InitGoogleMock(&argc, argv);

  3.         MockIAPIProviderInterface* iAPIProvider = new MockIAPIProviderInterface;

  4.         InSequence dummy;
  5.         EXPECT_CALL(*iAPIProvider, queryInterface(_)).Times(AtLeast(1)).
  6.                 WillRepeatedly(Return(1));

  7.         Rank rank;
  8.         rank.processQuery(iAPIProvider);

  9.         delete iAPIProvider;

  10.         return EXIT_SUCCESS;
  11. }
  • 这里的调用就相对简单了,只要一个MockIAPIProviderInterface就可以了。
Google Mock Cookbook

这里根据Google Mock Cookbook和我自己试用的一些经验,整理一些试用方面的技巧。

Mock protected、private方法

Google Mock也可以模拟protectedprivate方法,比较神奇啊(其实从这点上也可以看出,Mock类不是简单地继承原本的接口,然后自己把它提供的方法实现;Mock类其实就等于原本的接口)。
protectedprivate方法的Mock和public基本类似,只不过在Mock类中需要将这些方法设置成public
Foo.h 带private方法的接口

  1. class Foo {
  2. private:
  3.         virtual void setValue(int value) {};

  4. public:
  5.         int value;
  6. };

MockFoo.h

  1. class MockFoo: public Foo {
  2. public:
  3.         MOCK_METHOD1(setValue, void(int value));
  4. };
Mock 模版类(Template Class)

Google Mock可以Mock模版类,只要在宏MOCK*的后面加上T。
还是类似上述那个例子:
Foo.h 改成模版类

  1. template <typename T>
  2. class Foo {
  3. public:
  4.         virtual void setValue(int value) {};

  5. public:
  6.         int value;
  7. };

MockFoo.h

  1. template <typename T>
  2. class Foo {
  3. public:
  4.         virtual void setValue(int value) {};

  5. public:
  6.         int value;
  7. };
Nice Mocks 和 Strict Mocks

当在调用Mock类的方法时,如果之前没有使用EXPECT_CALL来定义该方法的期望行为时,Google Mock在运行时会给你一些警告信息:

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

对于这种情况,可以使用NiceMock来避免:

  1.         // MockFoo mockFoo;
  2.         NiceMock<MockFoo> mockFoo;
使用NiceMock来替代之前的MockFoo。

当然,另外还有一种办法,就是使用StrictMock来将这些调用都标为失败:

  1. StrictMock<MockFoo> mockFoo;

这时得到的结果:

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

最后

以上就是长情荔枝为你收集整理的google mock C++单元测试框架的全部内容,希望文章能够帮你解决google mock C++单元测试框架所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部