Google C++ Mocking Framework使用简介
2008-12-30 20:05
429 查看
安装:
下载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要作一点修改,同样过几天可以到http://www.cpp-prog.com/下载
注: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定义如下:
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仿制品:
首先加入包含文件
#include <gtest/gtest.h>
#include <gmock/gmock.h>
制作仿制品:
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如下:
//我们写的绘图类,因为要使用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;
};
我们测试一下它画正方形的功能是否正常:
using testing::AtLeast;
using testing::Return;
using testing::_;
using testing::Gt;
using testing::Eq;
TEST(PainterTest, SquareTest)
{
MockTurtle turtle;
EXPECT_CALL(turtle, Forward(_)) //预计将不再调Forward(),是倒序的,看下一语句
.Times(0);
EXPECT_CALL(turtle, Forward(Gt(0))) //预计将会先调用四次Forward(),其中参数大于0,是倒序的
.Times(4);
EXPECT_CALL(turtle, Turn(90)) //预计将会调用四次Turn(90)
.Times(4);
EXPECT_CALL(turtle, PenUp());
EXPECT_CALL(turtle, PenDown());
CPainter pt;
pt.SetTurtle(&turtle);
pt.Square(10); //测试输入10的情况,应该会调用四次Forward(10)和四次Turn(90)
pt.Square(0); //测试输入0的情况,应该不会再调用Forward才对
}
int main(int argc, char* argv[])
{
testing::InitGoogleMock(&argc, argv); //和Google Test使用方法一样,具体参考<Google C++ Testing Framework使用介绍>
int r = RUN_ALL_TESTS();
std::cin.get();
return r;
}
运行,测试通过(不知为什么看到一片绿让我想起了股市)
下面来解释一下Google Mock新引入的断言EXPECT_CALL,它就是整个Mock测试的关键:
EXPECT_CALL的语法是:
EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
看一下这个测试:(EXPECT_EQ的使用见<Google C++ Testing Framework使用介绍>)
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。
怎样测试带参数的方法呢?
EXPECT_CALL(turtle, Forward(100)); 表示预计将会调用turtle.Forward(100)。
EXPECT_CALL(turtle, Forward(testing::_)); 表示预计将会调用turtle.Forward,里面的参数可以任意。
EXPECT_CALL(turtle, Forward(testing::Ge(100))); 表示预计将会调用turtle.Forward,而且里面的参数都会大于或等于100。
将会被调用多少次?即Times(cardinality)中的cardinality使用方法
test::AtLeast(n)表示至少会调用n次
test::AtMost(n)表示至多会调用n次
更多:http://code.google.com/p/googlemock/wiki/CheatSheet
如果不写Times(cardinality),Google Mock将会自己推断出cardinality:
如果既没有WillOnce也没有WillRepeatedly,那么相当于Times(1)
有n个WillOnce但没有WillRepeatedly,那么相当于Times(n)
有n个WillOnce和一个WillRepeatedly,那么相当于Times(AtLeast(n))
多次预测:
假设有这样一个测试:
using testing::_;...
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
如果Forward(10)被调用了3次,那么第三次调用将被指出是一个错误,因为#2的测试不通过(说了是两次嘛,怎么出现三次呢-_-);如果第三次调用改成Forward(20)则没有问题,因为这次与#1匹配了。
再次注意,预测优先级顺序是反着来的,先对比#2再对比#1。
接顺序预测:
默认对于多个不同的预测是没有顺序要求的,只要每个预测达到要求就可以。如果你想要精确指定顺序,很简单:
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的顺序不致将报告错误。
所有的预测都是有“粘性”的
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))那次)匹配,结果当然是“超出预测的调用次数”;
正确的方法是明确指出预测不该存在粘性,另一种说法是当它们“吃饱”后就尽快“退役”:
using testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
另外,这种情况下我们还有更好一点的方法来指定序列:
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,解压...
发现它自带了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要作一点修改,同样过几天可以到http://www.cpp-prog.com/下载
注: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定义如下:
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仿制品:
首先加入包含文件
#include <gtest/gtest.h>
#include <gmock/gmock.h>
制作仿制品:
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如下:
//我们写的绘图类,因为要使用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;
};
我们测试一下它画正方形的功能是否正常:
using testing::AtLeast;
using testing::Return;
using testing::_;
using testing::Gt;
using testing::Eq;
TEST(PainterTest, SquareTest)
{
MockTurtle turtle;
EXPECT_CALL(turtle, Forward(_)) //预计将不再调Forward(),是倒序的,看下一语句
.Times(0);
EXPECT_CALL(turtle, Forward(Gt(0))) //预计将会先调用四次Forward(),其中参数大于0,是倒序的
.Times(4);
EXPECT_CALL(turtle, Turn(90)) //预计将会调用四次Turn(90)
.Times(4);
EXPECT_CALL(turtle, PenUp());
EXPECT_CALL(turtle, PenDown());
CPainter pt;
pt.SetTurtle(&turtle);
pt.Square(10); //测试输入10的情况,应该会调用四次Forward(10)和四次Turn(90)
pt.Square(0); //测试输入0的情况,应该不会再调用Forward才对
}
int main(int argc, char* argv[])
{
testing::InitGoogleMock(&argc, argv); //和Google Test使用方法一样,具体参考<Google C++ Testing Framework使用介绍>
int r = RUN_ALL_TESTS();
std::cin.get();
return r;
}
运行,测试通过(不知为什么看到一片绿让我想起了股市)
下面来解释一下Google Mock新引入的断言EXPECT_CALL,它就是整个Mock测试的关键:
EXPECT_CALL的语法是:
EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
看一下这个测试:(EXPECT_EQ的使用见<Google C++ Testing Framework使用介绍>)
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。
怎样测试带参数的方法呢?
EXPECT_CALL(turtle, Forward(100)); 表示预计将会调用turtle.Forward(100)。
EXPECT_CALL(turtle, Forward(testing::_)); 表示预计将会调用turtle.Forward,里面的参数可以任意。
EXPECT_CALL(turtle, Forward(testing::Ge(100))); 表示预计将会调用turtle.Forward,而且里面的参数都会大于或等于100。
将会被调用多少次?即Times(cardinality)中的cardinality使用方法
test::AtLeast(n)表示至少会调用n次
test::AtMost(n)表示至多会调用n次
更多:http://code.google.com/p/googlemock/wiki/CheatSheet
如果不写Times(cardinality),Google Mock将会自己推断出cardinality:
如果既没有WillOnce也没有WillRepeatedly,那么相当于Times(1)
有n个WillOnce但没有WillRepeatedly,那么相当于Times(n)
有n个WillOnce和一个WillRepeatedly,那么相当于Times(AtLeast(n))
多次预测:
假设有这样一个测试:
using testing::_;...
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
如果Forward(10)被调用了3次,那么第三次调用将被指出是一个错误,因为#2的测试不通过(说了是两次嘛,怎么出现三次呢-_-);如果第三次调用改成Forward(20)则没有问题,因为这次与#1匹配了。
再次注意,预测优先级顺序是反着来的,先对比#2再对比#1。
接顺序预测:
默认对于多个不同的预测是没有顺序要求的,只要每个预测达到要求就可以。如果你想要精确指定顺序,很简单:
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的顺序不致将报告错误。
所有的预测都是有“粘性”的
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))那次)匹配,结果当然是“超出预测的调用次数”;
正确的方法是明确指出预测不该存在粘性,另一种说法是当它们“吃饱”后就尽快“退役”:
using testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
另外,这种情况下我们还有更好一点的方法来指定序列:
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使用简介 安装:
- What Is Google C++ Mocking Framework?
- Google Test(Google C++ Testing Framework)简介
- Google C++ Testing Framework简介
- Google C++ Testing Framework 简介
- 使用Google C++ Testing Framework进行单元测试
- Google C++ Mocking Framework Cheat Sheet
- Google Mock启蒙篇 [2] (Google C++ Mocking Framework for Dummies 翻译)
- Google Mock启蒙篇 [1] (Google C++ Mocking Framework for Dummies 翻译)
- Google App Engine使用简介
- c++ bitset类的使用和简介
- Google C++ Style中允许使用的Boost库(1) - 程序即人生 - 博客频道 - CSDN.NET
- C++使用POST方法向网页提交数据-----C++发送HTTP数据获取Google天气预报
- 使用Google Unit Test 测试你的C++代码
- CxxTest使用简介 ——VS2008中使用C++单元测试框架
- C++ bitset类的使用与简介
- C++单元测试工具CppUnit使用简介
- CAF(C++ actor framework)使用随笔(使用类去构建actor和使用的一些思路)
- Jena 简介:通过 Jena Semantic Web Framework 在 Jave 应用程序中使用 RDF 模型
- Google App Engine使用简介