Google C++ Mocking Framework使用简介 安装:

Google C++ Mocking Framework使用简介

安装:

下载Google C++ Mocking Framework,解压...
发现它自带了Google Test的全部源代码,也就是说有了这个那个Google Test就不用去下载了
注意,Google Mock的编译对编译器的C++支持要求很高,并且需要有tr1的支持。

Linux/Unix下的GCC编译:

 注意:Google Mock的Readme里说它要求4.0版以上(不过版本低也没事,毕竟gcc是支持C++标准最好的编译器了,见Mingw3.45的安装)
 传统过程: ./configure make

Windows:

    Windows下的编译器自带tr1的还不多,我知道的也就BCB2009了,所以需要去下载一个Boost下来,好在gmock只用到了几个简单功能,所以不用编译Boost,直接包含Boost的目录,以及\boost\tr1\tr1即可。

VC2005 SP1: 

    打开msvc目录里的sln工程
设置包含路径,加入Boost, 加入Boost\tr1\tr1, 加入gtest所在路径
编译,搞定(跟着MS就是好混啊)

Mingw3.45:

    主要是google test要作一点修改,文章最后会提供下载。

注:google mock文档说要gcc4.0以上版本,而现在Mingw还没出4.0以上的稳定版,所以我在3.45版里发现使用时需要屏蔽gmock-matcher.h里的template<typename T> Matcher<T>::Matcher<T value>才行(在175行和1675行)。不过这样就有个缺点,象EXPECT_CALL(turtle, Turn(90))就得改成EXPECT_CALL(turtle, Turn(Eq(90)))了。
 

BCC:

    BCC就比较郁闷了,有太多地方不兼容了,偶搞不定呀呀呀:-(

  介绍:Google Mock是干什么的?

Google Mock的设计灵感来源于jMock和EasyMock,它的作用是帮你快速地做出一个接口的仿制品。如果你的设计依赖其它的类,而这些类还没有完成或非 常昂贵(如数据库);如果你要测试你的模块与其它模块是否能正确结合,并想了解其交互过程;那么Google Mock就能帮助你。

假设我们写好了一个CPainter类,它可以画各种图形。但它用到了一个Turtle类,它是由别人写的,而那小子光顾着和PLMM聊天了,现在还没开始动笔呢~!@#$%
那么想测试这个CPainter只有两个方法,一个是等那家伙把Turtle写好,另一个是自己写一个Turtle的仿制品出来用用先。
如果你选的是方法二,那么Google Mock就可以帮上忙了,假设Turtle定义如下:

  1. struct Turtle {   //这个功能可能是别人做的,现在还没完工 
  2.     virtual ~Turtle(){}; 
  3.     virtual void PenUp() = 0;  //起笔 
  4.     virtual void PenDown() = 0;//下笔 
  5.     virtual void Forward(int distance) = 0;  //前进 
  6.     virtual void Turn(int degrees) = 0;  //转向 
  7.     virtual void GoTo(int x, int y) = 0; //直接移动到指定位置 
  8.     virtual int GetX() const = 0; //获得当前位置 
  9.     virtual int GetY() const = 0; 
  10. }; 
struct Turtle { //这个功能可能是别人做的,现在还没完工 virtual ~Turtle(){}; virtual void PenUp() = 0; //起笔 virtual void PenDown() = 0;//下笔 virtual void Forward(int distance) = 0; //前进 virtual void Turn(int degrees) = 0; //转向 virtual void GoTo(int x, int y) = 0; //直接移动到指定位置 virtual int GetX() const = 0; //获得当前位置 virtual int GetY() const = 0; };

我们现在就用Google Mock写一个Turtle仿制品:
首先加入包含文件

  1. #include <gtest/gtest.h>
  2. #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gmock/gmock.h>

制作仿制品:

  1. struct MockTurtle : public Turtle { 
  2.     MOCK_METHOD0(PenUp, void()); 
  3.     MOCK_METHOD0(PenDown, void()); 
  4.     MOCK_METHOD1(Forward, void(int distance)); 
  5.     MOCK_METHOD1(Turn, void(int degrees)); 
  6.     MOCK_METHOD2(GoTo, void(int x, int y)); 
  7.     MOCK_CONST_METHOD0(GetX, int()); 
  8.     MOCK_CONST_METHOD0(GetY, int()); 
  9. }; 
struct MockTurtle : public Turtle { MOCK_METHOD0(PenUp, void()); MOCK_METHOD0(PenDown, void()); MOCK_METHOD1(Forward, void(int distance)); MOCK_METHOD1(Turn, void(int degrees)); MOCK_METHOD2(GoTo, void(int x, int y)); MOCK_CONST_METHOD0(GetX, int()); MOCK_CONST_METHOD0(GetY, int()); };

它从Turtle继承,把想要仿制的方法用MOCK_METHODn来定义(如果是const方法,则用MOCK_CONST_METHODn),这里的n是类方法的参数数量,第一个参数是方法名,第二个参数是此方法的函数类型(看看,不是类成员函数类型哦)

 好了,我们不用写一句代码,Google Mock已经帮我们把Turtle的仿制品准备好了,我们只管调用就可以了。如果接口方法很多,你还可以用scripts/generator/里的gmock_gen.py来帮你做这些工作(你需要安装Python 2.4)。这是一个命令行工具,你给它写有抽象类定义的C++文件,它就给你一个相应的Mock类。

现在可以测试我们的CPainter了,假设我们写的CPainter如下:

  1. //我们写的绘图类,由于要用到Turtle,
  2. //想测试的这个绘图类就得用上MockTurtle类了 
  3. struct CPainter{ 
  4.     CPainter():m_ptl(NULL){;} 
  5.     void SetTurtle(Turtle* ptl){ 
  6.         m_ptl = ptl; 
  7.     } 
  8.     void Square(int w) //画正方形 
  9.     { 
  10.         if(!m_ptl || w<=0) return; 
  11.         m_ptl->PenDown(); 
  12.         m_ptl->Forward(w); 
  13.         m_ptl->Turn(90); 
  14.         m_ptl->Forward(w); 
  15.         m_ptl->Turn(90); 
  16.         m_ptl->Forward(w); 
  17.         m_ptl->Turn(90); 
  18.         m_ptl->Forward(w); 
  19.         m_ptl->Turn(90); 
  20.         m_ptl->PenUp(); 
  21.     } 
  22. private: 
  23.     Turtle *m_ptl; 
  24. }; 
//我们写的绘图类,由于要用到Turtle, //想测试的这个绘图类就得用上MockTurtle类了 struct CPainter{ CPainter():m_ptl(NULL){;} void SetTurtle(Turtle* ptl){ m_ptl = ptl; } void Square(int w) //画正方形 { if(!m_ptl || w<=0) return; m_ptl->PenDown(); m_ptl->Forward(w); m_ptl->Turn(90); m_ptl->Forward(w); m_ptl->Turn(90); m_ptl->Forward(w); m_ptl->Turn(90); m_ptl->Forward(w); m_ptl->Turn(90); m_ptl->PenUp(); } private: Turtle *m_ptl; };

我们测试一下它画正方形的功能是否正常:

  1. using testing::AtLeast; 
  2. using testing::Return; 
  3. using testing::_; 
  4. using testing::Gt; 
  5. using testing::Eq; 
  6. TEST(PainterTest, SquareTest) 
  7.     MockTurtle turtle; 
  8.     //下面两句的意思是:
  9.     //预计调用完四次参数大于0的Forward()以后
  10.     //将不会再被调用,注意默认是倒序的,后面的先满足
  11.     EXPECT_CALL(turtle, Forward(_)) 
  12.         .Times(0); 
  13.     EXPECT_CALL(turtle, Forward(Gt(0))) 
  14.         .Times(4); 
  15.  
  16.     //预计将会调用四次Turn(90) 
  17.     EXPECT_CALL(turtle, Turn(90)) 
  18.         .Times(4); 
  19.     //也应该会调用PenUp和PenDown
  20.     EXPECT_CALL(turtle, PenUp()); 
  21.     EXPECT_CALL(turtle, PenDown()); 
  22.  
  23.     //开始调用
  24.     CPainter pt; 
  25.     pt.SetTurtle(&turtle); 
  26.     //测试输入10的情况,应该会调用四次Forward(10)和四次Turn(90) 
  27.     pt.Square(10);
  28.     //测试输入0的情况,应该不会调用Forward才对 
  29.     pt.Square(0);  
  30.  
  31. int main(int argc, char* argv[]) 
  32.     //和Google Test使用方法一样
  33.     //具体参考上一篇<使用Google C++ Testing Framework进行C++单元测试 > 
  34.     testing::InitGoogleMock(&argc, argv);  
  35.     int r = RUN_ALL_TESTS(); 
  36.     std::cin.get();  
  37.     return r; 
using testing::AtLeast; using testing::Return; using testing::_; using testing::Gt; using testing::Eq; TEST(PainterTest, SquareTest) { MockTurtle turtle; //下面两句的意思是: //预计调用完四次参数大于0的Forward()以后 //将不会再被调用,注意默认是倒序的,后面的先满足 EXPECT_CALL(turtle, Forward(_)) .Times(0); EXPECT_CALL(turtle, Forward(Gt(0))) .Times(4); //预计将会调用四次Turn(90) EXPECT_CALL(turtle, Turn(90)) .Times(4); //也应该会调用PenUp和PenDown EXPECT_CALL(turtle, PenUp()); EXPECT_CALL(turtle, PenDown()); //开始调用 CPainter pt; pt.SetTurtle(&turtle); //测试输入10的情况,应该会调用四次Forward(10)和四次Turn(90) pt.Square(10); //测试输入0的情况,应该不会调用Forward才对 pt.Square(0); } int main(int argc, char* argv[]) { //和Google Test使用方法一样 //具体参考上一篇<使用Google C++ Testing Framework进行C++单元测试 > testing::InitGoogleMock(&argc, argv); int r = RUN_ALL_TESTS(); std::cin.get(); return r; }

运行,测试通过(不知为什么看到一片绿让我想起了股市)

\

下面来解释一下Google Mock新引入的断言EXPECT_CALL,它就是整个Mock测试的关键:

EXPECT_CALL的语法是:
  1. EXPECT_CALL(mock_object, method(matchers))
  2.     .Times(cardinality)
  3.     .WillOnce(action)
  4.     .WillRepeatedly(action);
EXPECT_CALL(mock_object, method(matchers)) .Times(cardinality) .WillOnce(action) .WillRepeatedly(action);

看一下这个测试:(EXPECT_EQ的使用见<使用Google C++ Testing Framework进行C++单元测试 >)

  1. TEST(PainterTest, CanDrawSomething) { 
  2.     MockTurtle turtle; 
  3.     EXPECT_CALL(turtle, GetX())  //这个是写在前面地,就是说预计它应该会 
  4.             .Times(AtLeast(5))      //调用至少5次 
  5.         .WillOnce(Return(100))  //第一次调用GetX()就返回100 
  6.         .WillOnce(Return(150))  //第二次调用GetX()就返回150 
  7.         .WillRepeatedly(Return(200)) //接下来的所有调用就返回200 
  8.         ; 
  9.   
  10.     EXPECT_EQ(100, turtle.GetX());  //第一次运行turtle.GetX(),返回100 
  11.     EXPECT_EQ(150, turtle.GetX());  //第二次返回150 
  12.     EXPECT_EQ(200, turtle.GetX());  //第三次返回200 
  13.     EXPECT_EQ(200, turtle.GetX());  //第四次返回200 
  14.     EXPECT_EQ(20, turtle.GetX());   //第五次返回200,不信写个20试试这次测试是什么结果? 
  15. }
TEST(PainterTest, CanDrawSomething) { MockTurtle turtle; EXPECT_CALL(turtle, GetX()) //这个是写在前面地,就是说预计它应该会 .Times(AtLeast(5)) //调用至少5次 .WillOnce(Return(100)) //第一次调用GetX()就返回100 .WillOnce(Return(150)) //第二次调用GetX()就返回150 .WillRepeatedly(Return(200)) //接下来的所有调用就返回200 ; EXPECT_EQ(100, turtle.GetX()); //第一次运行turtle.GetX(),返回100 EXPECT_EQ(150, turtle.GetX()); //第二次返回150 EXPECT_EQ(200, turtle.GetX()); //第三次返回200 EXPECT_EQ(200, turtle.GetX()); //第四次返回200 EXPECT_EQ(20, turtle.GetX()); //第五次返回200,不信写个20试试这次测试是什么结果? }

测试结果:

\

 首先EXPECT_CALL是写在所有对turtle的调用之前的,也就是说EXPECT_CALL是一个预测,在这个测试结束时预测必须和实际情况相同,否则就Google Test就会发表意见
这个测试看上去很直观,意思是GetX至少会被调用5次,第一次调用时这个仿制品.GetX()返回100,第二次返回150,接下去就一直返回200。

怎样测试带参数的方法呢?
  1. //表示预计将会调用turtle.Forward(100)。
  2. EXPECT_CALL(turtle, Forward(100)); 
  3. //表示预计将会调用turtle.Forward,里面的参数可以任意。
  4. EXPECT_CALL(turtle, Forward(testing::_)); 
  5. //表示预计将会调用turtle.Forward,而且里面的参数都会大于或等于100。
  6. EXPECT_CALL(turtle, Forward(testing::Ge(100))); 
//表示预计将会调用turtle.Forward(100)。 EXPECT_CALL(turtle, Forward(100)); //表示预计将会调用turtle.Forward,里面的参数可以任意。 EXPECT_CALL(turtle, Forward(testing::_)); //表示预计将会调用turtle.Forward,而且里面的参数都会大于或等于100。 EXPECT_CALL(turtle, Forward(testing::Ge(100)));  将会被调用多少次?即Times(cardinality)中的cardinality使用方法
  1. //表示至少会调用n次
  2. test::AtLeast(n)
  3. //表示至多会调用n次
  4. test::AtMost(n)
//表示至少会调用n次 test::AtLeast(n) //表示至多会调用n次 test::AtMost(n)

更多请参考:http://code.google.com/p/googlemock/wiki/CheatSheet

如果不写Times(cardinality),Google Mock将会自己推断出cardinality:

  1. 如果既没有WillOnce也没有WillRepeatedly,那么相当于Times(1)
  2. 有n个WillOnce但没有WillRepeatedly,那么相当于Times(n)
  3. 有n个WillOnce和一个WillRepeatedly,那么相当于Times(AtLeast(n))
多次预测:

假设有这样一个测试:

  1. using testing::_;... 
  2. EXPECT_CALL(turtle, Forward(_));  // #1 
  3. EXPECT_CALL(turtle, Forward(10))  // #2 
  4.     .Times(2); 
using testing::_;... EXPECT_CALL(turtle, Forward(_)); // #1 EXPECT_CALL(turtle, Forward(10)) // #2 .Times(2);

如果Forward(10)被调用了3次,那么第三次调用将被指出是一个错误,因为#2的测试不通过(说了是两次嘛,怎么出现三次呢-_-);如果第三次调用改成Forward(20)则没有问题,因为这次与#1匹配了。
再次注意,预测优先级顺序是反着来的,先对比#2再对比#1。

接顺序预测:

默认对于多个不同的预测是没有顺序要求的,只要每个预测达到要求就可以。如果你想要精确指定顺序,很简单:

  1. using testing::InSequence;... 
  2. TEST(FooTest, DrawsLineSegment) { 
  3.   ... 
  4.   { 
  5.     InSequence dummy; 
  6.   
  7.     EXPECT_CALL(turtle, PenDown()); 
  8.     EXPECT_CALL(turtle, Forward(100)); 
  9.     EXPECT_CALL(turtle, PenUp()); 
  10.   } 
  11.   Foo(); 
  12. }
using testing::InSequence;... TEST(FooTest, DrawsLineSegment) { ... { InSequence dummy; EXPECT_CALL(turtle, PenDown()); EXPECT_CALL(turtle, Forward(100)); EXPECT_CALL(turtle, PenUp()); } Foo(); }

所有在InSequence生存空间内放入的预测都将严格按顺序测试,如果调用PenDown,Forward,PenUp的顺序不致将报告错误。

所有的预测都是有“ 粘性”的
  1. using testing::Return; 
  2. ... 
  3. for (int i = n; i > 0; i--) { 
  4.   EXPECT_CALL(turtle, GetX()) 
  5.       .WillOnce(Return(10*i)); 
using testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)); }

如果你认为这段预测代表turtle.GetX()将被调用n次,而且依次是10,20,30...,错!因为预测是有“粘性”的,第二次调用GetX时,还是与最后一次预测(也就是EXPECT_CALL(turtle, GetX()).WillOnce(Return(10))那次)匹配,结果当然是“超出预测的调用次数”;
正确的方法是明确指出预测不该存在粘性,另一种说法是当它们“吃饱”后就尽快“退役”:

  1. using testing::Return; 
  2. ... 
  3. for (int i = n; i > 0; i--) { 
  4.   EXPECT_CALL(turtle, GetX()) 
  5.     .WillOnce(Return(10*i)) 
  6.     .RetiresOnSaturation(); 
using testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); }

另外,这种情况下我们还有更好一点的方法来指定序列:

  1. using testing::InSequence; 
  2. using testing::Return; 
  3. ... 
  4.   InSequence s; 
  5.   
  6.   for (int i = 1; i <= n; i++) { 
  7.     EXPECT_CALL(turtle, GetX()) 
  8.         .WillOnce(Return(10*i)) 
  9.         .RetiresOnSaturation(); 
  10.   } 
using testing::InSequence; using testing::Return; ... { InSequence s; for (int i = 1; i <= n; i++) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); } } 附: Google C++ Mocking Framework for Mingw 下载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值