gmock学习一 转载

Google Mock启蒙篇 [1] (Google C++ Mocking Framework for Dummies 翻译)

Google C++ Mocking Framework for Dummies

Google Mock启蒙篇

Version: 0.07< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />

作者:adrian alexander

译者:Koala++ / 屈伟

最新PDF版下载

What Is Google C++ Mocking Framework

当你写一个原型或是测试的时候,直接去依赖真实的对象通常是不可行的或是不明智的。Mock对象实现与真实对象有着相同的接口,但你可以去指定Mock对象在运行时它做什么( 比如,调用哪个函数,以什么顺序,调用多少次,使用什么参数,返回内容是什么,等等 )。

注意Fake对象Mock对象两个术语很容易混淆。在测试驱动开发( TDD )语境下,Fake对象和Mock对象是区别很大的两个概念。

l  Fakes是有具体实现的,但通常是一些走了捷径的实现,所以它不能真正的用于发布产品中。比如一个在内存中的文件系统就是一个Fake的例子。

l  Mocks是一些有期望( expections )预先编程的对象,期望形成了有着期望结果的调用的一个特化。

上面所解释也许太过抽象,但别担心,最重要的就是记住:Mock可以使你检查它与使用它的代码之间的交互。其实,当你开始使用Mocks之后,Fakes和Mocks之间的区别会马上清晰起来。

Google C++ Mocking Framework ( 或简称Google Mock )是一个用于创建Mock类使用这些类的库( 有时我们也将它称为框架,让它听起来更酷一些 )。它和Java世界中的jMockEasyMock功能相同。

使用Google Mock有下面三个基本步骤:

1.  使用简单的宏来描述你想Mock的接口,这些宏会自动扩展成你的Mock类的实现。

2.  创建一些Mock对象,并用一种直观的语法去指定Mock对象的期望和行为。

3.  用这些Mock对象测试代码。Google Mock会捕获那些违反期望的冲突。

Why Google Mock?

虽然Mock对象可以帮助你在测试中去除不必要的依赖,并使测试更快更可靠,但是在C++中用Mocks却比较难。

l  它需要你自己去实现Mocks。这项工作通常无聊并且易错。无怪大家都尽可能不去做这种件事。

l  这些自己实现的Mocks质量有一点……,嗯,不可预测。你可能见过一些写的很精巧的Mocks,但你也可能看到一些匆忙间hack出来的Mocks,这些Mocks中充斥着各种怪异的限制。

l  你在使用一个Mock所获得的经验无法在下一个Mock中使用。

相反,Java和Python程序员有一些很好的Mock框架,它们可以自动创建Mocks。结果,在Java和Python世界Mocking是被证明是一种高效的技术,并被广泛地使用。

Google Mock就是为帮助C++程序员解决Mock问题而生的。它受jMockEasyMock的启发,但在设计时思虑了C++的特性。如果下面的任一问题困扰着你,那么Google Mock将成为你的朋友。

l  你被一个优化了一部分的设计困住了,你希望可以做一些原型设计,以免时间不够了。但原型设计在C++中绝对不可能称之为快。

l  你的测试很慢可能它们依赖很多库或是使用很多资源( 比如,数据库 )。

l  你的测试很脆弱,因为它所依赖的资源不可靠。

l  你想测试你的代码处理失败的情况,但是很难产生一个失败。

l  你需要保证你的模块与别的模块以正确的方式交互,但很难看到交互的过程,你只能看到最终运行的结果,这无论如何都是尴尬的。

l  你想要“Mock out”你的依赖,但是这些依赖还没有Mock实现,坦白地讲,就算是有,你看到这些手写的Mocks也会头痛。

我们推荐你在以下面的方式使用Google Mock:

l  作为一个设计工具,因为它可以让你更早更频繁地试验你的接口设计。更多的迭代会产生更好的设计。

l  作为一个测试工具,它可以解除外围的依赖,并可以查看你的模板与其它模块的交互。

Get Started

使用Google Mock很容易!在你的C++源文件中,只需要写上#include “gtest/gtest.h”和“gmock/gmock.h”,你就可以开始你的Goole Mock之旅了。

A Case for Mock Turtles

让我们看一个例子。假设你在开发一个图形程序,它依赖一个类似Logo( 译注:初一我学的第一门计算机语言,每次我听到它名字都会激动万分,虽然它的命令我几乎忘光了)的API来绘图,你怎么去测试你的程序是正确的呢?嗯,你可以运行它,然后比较你屏幕上的结果和目标屏幕截图,但是必需要承认的是:这种测试很麻烦,并且健壮性不足( 如果你升级了你的显卡,这个显卡有更好的抗锯齿能力,那你需要把你用的图形文件都换了)。如果你的测试都是这样的,那你会很痛苦的。幸运的是,你知道依赖注入并且知道该如何去做:不要让你的程序直接去调用绘图API,而应该将API封装成一个接口( Turtle,译注:Logo语言中的图标像是一个海龟,在Doc时代这完全是骗小朋友的,它就是一个没有尾巴的箭头 ),并针对接口编程。

 1 class Turtle {
 2   ...
 3   virtual ~Turtle() {}
 4   virtual void PenUp() = 0;
 5   virtual void PenDown() = 0;
 6   virtual void Forward(int distance) = 0;
 7   virtual void Turn(int degrees) = 0;
 8   virtual void GoTo(int x, int y) = 0;
 9   virtual int GetX() const = 0;
10   virtual int GetY() const = 0;
11 };

注意:Turtle类的析构函数必须是虚函数,因为在随后的介绍中要继承这个类。

你可以通过PenUp()和PenDown()来控制光标的移动是否会留下痕迹,并用Forward(),Turn(),和Goto()来控制它的移动,GetX()和GetY()会告诉你当前光标的位置。

你的程序通常会使用这个接口的真实实现。但在测试中,你可以使用一个Mock实现来代替。这可以让你更早地检查你的程序调用哪些绘图API,使用什么参数,以什么顺序调用。这样的测试更健壮( 这样的测试不会因为你的新显卡在反锯齿性上表现不同而失败),并且这种代码更容易去理解和维护( 测试的目标是用代码表示,而不是用一些二进制图形去表示),而且会运行的非常非常快。

Writing the Mock Class

如果你需要的Mocks已经有好心人实现了,那你很走运。但是如果你发现需要自己要去实现Mock类,也别紧张,Google Mock已经将这个任务变成了一个有趣的游戏( 嗯,算是吧 )。

How to Define It

这里以Turtle接口为例子,下面是你需要去做的几个步骤:

1.  继承Turtle类得到MockTurtle类。

2.  从Turtle类中选一个虚函数( 也可用模板Mock非虚函数,但那涉及的知识就多了一些),数一下这个函数有几个参数。

3.  在MockTurtle的public:部分,写上MOCK_METHODn(); (如果你要Mock一个const函数,就写MOCK_CONST_METHODn ),其中n是函数中的参数个数,如果你真的连数数都能数错,那编译器会坦白地告诉你这个丢脸的事实。

4.  这一步终于是能看到意义的一步了:你把函数名作为宏的第一个参数,然后将函数定义中除函数名以外的部分作为宏的第二个参数。

5.  重复上述步骤,直到你想Mock的虚函数都Mock了。

在完成上述步骤后,你得到的是类似下面的代码:

 1 #include "gmock/gmock.h"  // Brings in Google Mock.
 2 class MockTurtle : public Turtle {
 3  public:
 4   ...
 5   MOCK_METHOD0(PenUp, void());
 6   MOCK_METHOD0(PenDown, void());
 7   MOCK_METHOD1(Forward, void(int distance));
 8   MOCK_METHOD1(Turn, void(int degrees));
 9   MOCK_METHOD2(GoTo, void(int x, int y));
10   MOCK_CONST_METHOD0(GetX, int());
11   MOCK_CONST_METHOD0(GetY, int());
12 };

你不需要再在别的地方去定义这些Mock函数了,MOCK_METHOD*宏会帮你产生这些函数定义。这很简单!一旦掌握了它的诀窍,你可以产生大量的Mock类,可能快到连源代码管理工具都处理不过来。

小提示:如果连定义对你来说工作量都太大,你可以在scripts/generator目录下找到一个gmock_gen.py工具,这个命令行工具需要安装Python 2.4。你将C++文件名和抽象类名作为参数传入这个工具,它会打印Mock类的定义给你。但是因为C++的复杂性,这个脚本还是可能出错,但当它不出错的时候,还是很方便的。更多的细节在用户文档中。

Where to Put It

当你定义一个Mock类,你需要决定把它的定义放到哪。一些人把它放到一个*_test.cc文件中。当这个接口(就叫Foo吧)是由同一个人或是同一团队维护时,这没什么问题。但如果不是,当Foo的维护者修改了它,你的测试就会编译不通过( 你总不能指望Foo的维护者去修改每个使用Foo的测试测试吧 )。

所以,经验法则是:如果你需要Mock Foo并且它由别人维护时,在Foo包中定义Mock类( 更好的做法是在测试包中定义它,这样可以将测试代码更清晰地独立出来),把它放到mock_foo.h中。那么每个想使用Mock Foo类的都可以在他们的测试代码中引用它。如果Foo改变了,那么只需要改一份MockFoo的代码,并且只有依赖这个变动函数的测试代码需要做相应的修改。

另一种做法是:你可以在Foo之上引入一个FooAdaptor层,并针对FooAdaptor这个新接口编程。因为你对FooAdaptor有控制权,你可以很容易地将Foo的改变隐藏掉。虽然这意味着在开始有更大的工作量,但认真构造的适配器接口会使你的代码更容易开发,也有更高的可读性,因为你构造的适配器接口FooAdaptor会比Foo更适合于你的特定领域开发。

Using Mocks in Tests

当你完成Mock类的定义之后,使用它是很简单的。典型的流程如下:

1.  引用那些你需要使用的Google Mock有关的命名空间( 这样你就不用每次都把命名空间加到前面,请牢记,使用命名空间是一个好主意,并且对你的健康大有裨益 )。

2.  创建一些Mock对象。

3.  对它们指定你的期望( 一个函数要被调用多少次? 用什么参数? 它返回什么? 等等)。

4.  用这些Mocks来测试一些代码。你可以选择Google Test Assertions来检查返回。如果一个Mock函数被调用次数多于期望,或是使用了错误的参数,你会马上得到一个错误提示。

5.  当一个Mock对象被析构时,Google Mock会自动检查在它上面的所有的期望是否都已经满足了。

下面是一个例子:

 1 #include "path/to/mock-turtle.h"
 2 #include "gmock/gmock.h"
 3 #include "gtest/gtest.h"
 4 using ::testing::AtLeast;                     // #1
 5
 6 TEST(PainterTest, CanDrawSomething) {
 7   MockTurtle turtle;                          // #2
 8   EXPECT_CALL(turtle, PenDown())              // #3
 9       .Times(AtLeast(1));
10
11   Painter painter(&turtle);                   // #4
12
13   EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
14 }                                             // #5
15
16 int main(int argc, char** argv) {
17   // The following line must be executed to initialize Google Mock
18   // (and Google Test) before running the tests.
19   ::testing::InitGoogleMock(&argc, argv);
20   return RUN_ALL_TESTS();
21 }

正如你所猜测的一样,这个测试是检查PenDown()是否被调用了至少一次。如果Painter对象并没有调用这个函数,你的测试就会失败,提示信息类似如下:

path/to/my_test.cc:119: Failure

Actual function call count doesn‘t match this expectation:

Actually: never called;

Expected: called at least once.

技巧1如果你从一个Emacs Buffer运行这个测试程序,你可以在错误信息的行号上敲Enter键,就可以直接跳到期望失败的那一行了。

技巧2: 如果你的Mock对象永不释放,最后的检查是不会发生的。所以当你在堆上分配Mock对象时,你用内存泄露工具检查你的测试是一个好主意( 译注:推荐valgrind )。

重要提示:Google Mock要求期望在Mock函数被调用之前就设置好,否则行为将是未定义的。特别是你绝不能在Mock函数调用中间插入EXPECT_CALL()。

这意味着EXPECT_CALL()应该被理解为一个调用在未来的期望,而不是已经被调用过函数的期望。为什么Google Mock要以这种方式工作呢?嗯……,在前面指定期望可以让Google Mock在异常发生时马上可以提示,这时候上下文( 栈信息,等等 )还是有效的。这样会使调试更容易。

要承认的是,这个测试没有展示出Google Mock有什么强大之处。你完全可以不用Google Mock来得到相同的效果。但是别急,在下面的展示中,我会让你看到Google Mock的强大,它可以让你用Mock做非常多的事。

Using Google Mock with Any Testing Framework

如果你在用别的测试框架而不是Google Test( 比如,CppUnitCxxUnit),只需要把上节中的main函数改成下面这样:

1 int main(int argc, char** argv) {
2   // The following line causes Google Mock to throw
3 // an exception on failure, which will be interpreted
4 // by your testing framework as a test failure.
5   ::testing::GTEST_FLAG(throw_on_failure) = true;
6   ::testing::InitGoogleMock(&argc, argv);
7   ... whatever your testing framework requires ...
8 }    

这种方法中有一个catch:它可以让Google Mock从Mock对象的析构函数中抛出一个异常。但有一些编译器,这会让测试程序崩溃( 译注:可以参考Effect C++第三版的Item 8)。虽然你仍然可以注意到注意失败了,但这绝不是一个优雅的失败方式。

一个更好的方法是用Google Test的event listener API来以合理的方式报告一个测试失败给你的测试框架。你需要实现OnTestPartResult()函数这个事件监听接口,但实现它也很简单。

如果上面的方法对你来说工作量太大,我建议你还是用Google Test吧,它与Google Mock可以无缝结合。如果你有什么Google Test满足不了你测试需求的原因,请告诉我们。

gmock学习一 转载

时间: 2024-11-07 18:07:14

gmock学习一 转载的相关文章

gmock学习二 转载

Google Mock启蒙篇 [2] (Google C++ Mocking Framework for Dummies 翻译) 2011-11-22 22:34:58|  分类: C++ |  标签:google  mock  测试  |举报|字号 订阅 Setting Expectations 成功地使用Mock对象的关键是在它上面设置合适的期望.如果你设置的期望太过严格,你的测试可能会因为无关的改变而失败.如果你把期望设置的太过松驰,bugs可能会溜过去.而你需要的是你的测试可以刚好捕获你

ARM指令集学习总结-转载

ARM指令集比较简单,本文介绍ARM指令集中需要注意和不易理解的地方. 一.ARM指令集是32位的,程序的启动都是从ARM指令集开始,包括所有异常中断都是自动转化为ARM状态,并且所有的指令都可以是有条件执行的.         二.ARM指令集是Load/Store型的,只能通过Load/Store指令实现对系统存储器的访问,而其他的指令都是基于处理器内部的寄存器操作完成的,这和INTEL汇编是不同的,初学者很不易理解.        三.指令的后缀:     "S"  可选后缀,若

MySql学习笔记(转载)

/* 启动MySQL */net start mysql /* 连接与断开服务器 */mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权限验证登录MySQL */mysqld --skip-grant-tables-- 修改root密码密码加密函数password()update mysql.user set password=password('root'); SHOW PROCESSLIST -- 显示哪些线程正在运行SHOW VARIABLES -- /* 数据库操

linux grep正则学习(转载)

虽然正则表达式经常都在用,但是很少能够静下心来仔细的总结一下.最近看了一个台湾人的网站叫做鸟哥Linux私房菜,关于正则表达式的描述挺详细的.在此,我进行一下总结,如果想仔细的学习正则表达式,请访问鸟哥Linux私房菜,台湾同胞的网站是繁体中文的,需要点儿耐心. 正则表达式只是字符串的一种描述,只有和支持正则表达式的工具相结合才能进行字符串处理.本文以grep为例来讲解正则表达式. grep命令 功能:输入文件的每一行中查找字符串. 基本用法: grep [-acinv] [--color=au

Github上安卓榜排名第2的程序员教你如何学习【转载,侵删】

来自:峰瑞资本(微信号:freesvc)文章作者:代码家(微信 ID:daimajia_share) 软件早已吞噬整个世界,程序员是关键角色.过去 40 年中,许多伟大的公司都由程序员缔造,比如比尔·盖茨.拉里·埃里森.马克·扎克伯格.伊隆·马斯克. 一本哥哥不太关心的 Visual Basic 课外书,代码家如获至宝,他接触了简单的代码,这让他进入到一个全新的世界.从那之后,代码家一直享受着用代码解决问题过程中产生的精神愉悦,并成长为一名专家级程序员.用行话说,黑客.现在,代码家是 Githu

Redis学习手册——转载

转载出处:http://www.cnblogs.com/stephen-liu74/archive/2012/04/16/2370212.html 为什么自己当初要选择Redis作为数据存储解决方案中的一员呢?现在能想到的原因主要有三.其一,Redis不仅性能高效,而且完全免费.其二,是基于C/C++开发的服务器,这里应该有一定的感情因素吧.最后就是上手容易,操作简单.记得在刚刚接触Redis的时候,由于当时项目的工期相当紧张,留给我们做出选择的空间也是非常有限,一旦技术决策失误,造成的后果也比

Hadoop家族学习路线图--转载

原文地址:http://blog.fens.me/hadoop-family-roadmap/ Sep 6, 2013 Tags: Hadoophadoop familyroadmap Comments: 27 Comments Hadoop家族学习路线图 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeeper, Avro, Ambari, Chukwa,新增加的项目包括,YARN

高手的C++学习忠告,虚心学习下~~[转载]

1.把C++当成一门新的语言学习(和C没啥关系!真的.): 2.看<Thinking In C++>,不要看<C++变成死相>: 3.看<The C++ Programming Language>和<Inside The C++ Object Model>,不要因为他们很难而我们自己是初学者所以就不看: 4.不要被VC.BCB.BC.MC.TC等词汇所迷惑——他们都是集成开发环境,而我们要学的是一门语言: 5.不要放过任何一个看上去很简单的小编程问题——他们

高手C++学习忠告~~[转载]

1.把C++当成一门新的语言学习(和C没啥关系!真的.): 2.看<Thinking In C++>,不要看<C++变成死相>: 3.看<The C++ Programming Language>和<Inside The C++ Object Model>,不要因为他们很难而我们自己是初学者所以就不看: 4.不要被VC.BCB.BC.MC.TC等词汇所迷惑——他们都是集成开发环境,而我们要学的是一门语言: 5.不要放过任何一个看上去很简单的小编程问题——他们