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世界中的jMock和EasyMock功能相同。

使用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问题而生的。它受jMock和EasyMock的启发,但在设计时思虑了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时代这完全是骗小朋友的,它就是一个没有尾巴的箭头 ),并针对接口编程。

class 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;

};

注意: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了。

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

#include "gmock/gmock.h"  // Brings in Google Mock.

class MockTurtle : public Turtle {

public:

...

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());

};

你不需要再在别的地方去定义这些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会自动检查在它上面的所有的期望是否都已经满足了。

下面是一个例子:

#include "path/to/mock-turtle.h"

#include "gmock/gmock.h"

#include "gtest/gtest.h"

using ::testing::AtLeast;                     // #1

TEST(PainterTest, CanDrawSomething) {

MockTurtle turtle;                          // #2

EXPECT_CALL(turtle, PenDown())              // #3

.Times(AtLeast(1));

Painter painter(&turtle);                   // #4

EXPECT_TRUE(painter.DrawCircle(0, 0, 10));

}                                             // #5

int main(int argc, char** argv) {

// The following line must be executed to initialize Google Mock

// (and Google Test) before running the tests.

::testing::InitGoogleMock(&argc, argv);

return RUN_ALL_TESTS();

}

正如你所猜测的一样,这个测试是检查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( 比如,CppUnit或CxxUnit ),只需要把上节中的main函数改成下面这样:

int main(int argc, char** argv) {

// The following line causes Google Mock to throw

// an exception on failure, which will be interpreted

// by your testing framework as a test failure.

::testing::GTEST_FLAG(throw_on_failure) = true;

::testing::InitGoogleMock(&argc, argv);

... whatever your testing framework requires ...

}

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

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

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

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

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

    Setting Expectations 成功地使用Mock对象的关键是在它上面设置合适的期望.如果你设置的期望太过严格,你的测试可能会因为无关的改变而失败.如果你把期望设置的太过松驰,bugs可能会 ...

  2. What Is Google C++ Mocking Framework?

    (Note: If you get compiler errors that you don't understand, be sure to consult Google Mock Doctor.) ...

  3. google mock分享

    Content Matcher(匹配器) 基数(Cardinalities) 行为(Actions) 序列(Sequences) Google Mock 入门 概述 Google Mock使用 Moc ...

  4. Google Mock 入门

    Google Mock 入门 原文 概述 什么是Mock? Mock,更确切地说应该是Mock Object.它究竟是什么?它有什么作用?在这里,我也只能先说说我的理解. 比如当我们在单元测试.模块的 ...

  5. Google Mock(Gmock)简单使用和源码分析——简单使用

    初识Gmock是之前分析GTest源码时,它的源码和GTest源码在同一个代码仓库中(https://github.com/google/googletest).本文我将以目前最新的Gmock1.7版 ...

  6. Google Mock(Gmock)简单使用和源码分析——源码分析

    源码分析 通过<Google Mock(Gmock)简单使用和源码分析--简单使用>中的例子,我们发现被mock的相关方法在mock类中已经被重新实现了,否则它们也不会按照我们的期待的行为 ...

  7. 用google mock模拟C++对象

    google mock是用来配合google test对C++项目做单元测试的.它依赖于googletest(参见我上篇文章<如何用googletest写单元测试>: http://blo ...

  8. Google Test(GTest)和Google Mock(GMock)入门简介

    Google Test 1. 自定义错误输出: ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal ...

  9. 《Google软件测试之道》- Google软件测试介绍

    <Google软件测试之道>- Google软件测试介绍 2015-05-21 目录 1 质量与测试   2 角色   3 组织结构   4 爬.走.跑   5 测试类型   相关链接 与 ...

最新文章

  1. The key of C# 学习笔记I-II
  2. zabbix3.2.6.1升级3.4.4图文心得
  3. win10上面安装win7的虚拟机怎么相互ping通
  4. python基础实例 韦玮 pdf_Python基础实例教程(微课版)
  5. java 开发环境的搭建
  6. python 定义字符串变量_Python变量和字符串详解
  7. html语言table,html中的table详解
  8. FastDFS介绍与安装配置
  9. echarts常用方法(一)
  10. mysql监听串口_tcp与串口透传(select)
  11. netbeans写登录界面java_NetBeans 界面美化与字体设置
  12. 软件工程概述思维导图总结(一)
  13. 小白能读懂的 《手把手教你学DSP(TMS320X281X)》第四章(2) gel文件
  14. 【Matlab创建word文档,插入图注或表注】
  15. Mysql 创建数据库\添加用户\用户授权
  16. Hulu斩获两枚艾美奖提名!(附第68届艾美奖重要奖项提名名单)
  17. Linux没有网怎么解决。
  18. nova青春版支持鸿蒙吗,华为nova青春版有NFC吗 华为nova青春版支持NFC功能吗
  19. Android软件开发之盘点界面五大布局(十六)
  20. Vue3 + vueRouter4.x 控制台No match found for location with path ‘/home‘ 解决

热门文章

  1. 101 Tips to MySQL Tuning and Optimization
  2. arm-linux-gdb正确无错误安装
  3. 网络语言对作文教学的冲击
  4. TCP协议的3次握手和4次挥手
  5. 2020CCPC(长春) - Combination Lock(二分图博弈)
  6. CodeForces - 1252D Find String in a Grid(AC自动机)
  7. POJ - 2778 DNA Sequence(AC自动机+矩阵快速幂)
  8. 扩展中国剩余定理(模板)
  9. c++ 随机字符串_关于Python的随机数模块,你必须要掌握!
  10. python 读取mongodb,python操作MongoDB