Setting Expectations

成功地使用Mock对象的关键是在它上面设置合适的期望。如果你设置的期望太过严格,你的测试可能会因为无关的改变而失败。如果你把期望设置的太过松驰,bugs可能会溜过去。而你需要的是你的测试可以刚好捕获你想要捕获的那一种bug。Google Mock提供了一些方法可以让你的测试尺度刚好( just right )。

General Syntax

在Goolge Mock中,我们用EXPECT_CALL()宏来设置一个Mock函数上的期望。一般语法是:

EXPECT_CALL(mock_object, method(matchers))< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />

.Times(cardinality)

.WillOnce(action)

.WillRepeatedly(action);

这个宏有两个参数:第一个是Mock对象,第二个参数是函数和它的参数。注意两个参数是用逗号( , )分隔的,而不是句号( . )。

这个宏可以跟一些可选子句,这些子句可以提供关于期望更多的信息。我们将会在下面的小节中介绍每个子句有什么意义。

这些语法设计的一个目的是让它们读起来像是英语。比如你可能会直接猜出下面的代码是有什么含义

using ::testing::Return;...

EXPECT_CALL(turtle, GetX())

.Times(5)

.WillOnce(Return(100))

.WillOnce(Return(150))

.WillRepeatedly(Return(200));

公布答案,turtle对象的GetX()方法会被调用5次,它第一次返回100,第二次返回150,然后每次返回200。许多人喜欢称这种语法方式为特定领域语言( Domain-Specific Language (DSL) )。

注意:为什么我们要用宏来实现呢?有两个原因:第一,它让期望更容易被认出来( 无论是grep还是人去阅读 ),第二,它允许Google Mock可以得到失败期望在源文件的位置,从而使Debug更容易。

Matchers: What Arguments Do We Expect?

当一个Mock函数需要带参数时,我们必须指定我们期望的参数的是什么;比如:

// Expects the turtle to move forward by 100 units.

EXPECT_CALL(turtle, Forward(100));

有时你可能不想指定的太精确( 还记得前面测试不应太严格吗?指定的太精确会导致测试健壮性不足,并影响测试的本意。所以我们鼓励你只指定那些必须要指定的参数,不要多,也不要少 )。如果你只关心Forward是否会被调用,而不关心它用什么参数,你可以写_作为参数,它的意义是“任意”参数。

using ::testing::_;

...

// Expects the turtle to move forward.

EXPECT_CALL(turtle, Forward(_));

_是我们称为Matchers的一个例子,一个matcher是像一个断言,它可测试一个参数是否是我们期望的。你可用在EXPECT_CALL()中任何写函数参数期望的地方用matcher。

一个内置的matchers可以在CheatSheet中找到,比如,下面是Ge( greater than or equal ) matcher的应用。

using ::testing::Ge;...

EXPECT_CALL(turtle, Forward(Ge(100)));

这个测试是检查turtle是否被告知要至少前进至少100个单位。

Cardinalities: How Many Times Will It Be Called?

在EXPECT_CALL()之后第一个我们可以指定的子句是Times()。我们称Times的参数为cardinality,因为它是指这个函数应该被调用多少次。Times可以让我们指定一个期望多次,而不用去写一次次地写这个期望。更重要的是,cardinality可以是“模糊”的,就像matcher一样。它可以让测试者更准确地表达他测试的目的。

一个有趣的特例是我们指定Times(0)。你也许已经猜到了,它是指函数在指定参数下不应该被调用,如果这个函数被调用了,Google Mock会报告一个Google Test失败。

我们已经见过AtLeast(n)这个模糊cardinalities的例子了。你可以在CheatSheet中找一个内置cardinalities列表。

Times()子句可以省略。如果你省略Times(),Google Mock会推断出cardinality的值是什么。这个规则很容易记:

l  如果在EXPECT_CALL中既没有WillOnce()也没有WillRepeatedly(),那推断出的cardinality就是Times(1)。

l  如果有n个WillOnce(),但没有WillRepeatedl(),其中n >= 1,那么cardinality就是Times(n)。

l  如果有n个WillOnce(),和一个WillRepeatedly(),其中n >= 0,那么cardinality就是Times(AtLeast(n))。

小测试:如果一个函数期望被调用2次,但被调用了4次,你认为会发生什么呢?

Actions: What Should It Do?

请记住一个Mock对象其实是没有实现的。是我们这些用户去告诉它当一个函数被调用时它应该做什么。这在Google Mock中是很简单的。

首先,如果Mock函数的返回类型是一个指针或是内置类型,那这个函数是有默认行为的( 一个void函数直接返回,bool函数返回false,其它函数返回0 )。如果你不想改变它,那这种行为就会被应用。

其次,如果一个Mock函数没有默认行为,或默认行为不适合你,你可以用WillOnce来指定每一次的返回值是什么,最后可以选用WillRepeatedly来结束。比如:

using ::testing::Return;...

EXPECT_CALL(turtle, GetX())

.WillOnce(Return(100))

.WillOnce(Return(200))

.WillOnce(Return(300));

上面的意思是turtle.GetX()会被调用恰好3次,并分别返回100,200,300。

using ::testing::Return;...

EXPECT_CALL(turtle, GetY())

.WillOnce(Return(100))

.WillOnce(Return(200))

.WillRepeatedly(Return(300));

上面的意思是指turtle.GetY()将至少被调用2次,第一次返回100,第二次返回200,从第三次以后都返回300。

当然,你如果你明确写上Times(),Google Mock不会去推断cardinality了。如果你指定的cardinality大于WillOnce()子句的个数时会发生什么呢?嗯,当WillOnce()用完了之后,Google Mock会每次对函数采用默认行为。

我们在WillOnce()里除了写Return()我们还能做些什么呢?你可以用ReturnRef( variable ),或是调用一个预先定义好的函数,自己在Others中找吧。

重要提示:EXPECT_CALL()只对行为子句求一次值,尽管这个行为可能出现很多次。所以你必须小心这种副作用。下面的代码的结果可能与你想的不太一样。

int n = 100;

EXPECT_CALL(turtle, GetX())

.Times(4)

.WillRepeatedly(Return(n++));

它并不是依次返回100,101,102...,而是每次都返回100,因为n++只会被求一次值。类似的,Return(new Foo)当EXPECT_CALL()求值时只会创建一个Foo对象,所以它会每次都返回相同的指针。如果你希望每次都看到不同的结果,你需要定义一个自定义行为,我们将在CookBook中指导你。

现在又是一个小测验的时候了!你认为下面的代码是什么意思?

using ::testing::Return;...

EXPECT_CALL(turtle, GetY())

.Times(4)

.WillOnce(Return(100));

显然,turtle.Get()期望被调用4次。但如果你认为它每次都会返回100,那你就要再考虑一下了!记住,每次调用都会消耗一个WillOnce()子句,消耗完之后,就会使用默认行为。所以正确的答案是turtle.GetY()第一次返回100,以后每次都返回0,因为0是默认行为的返回值。

Using Multiple Expectations

至今为止,我们只展示了如何使用单个期望。但是在现实中,你可能想指定来自不同Mock对象的Mock函数上的期望。

默认情况下,当一个Mock函数被调用时,Google Mock会通过定义顺序的逆序去查找期望,当找到一个与参数匹配的有效的期望时就停下来( 你可以把这个它想成是“老的规则覆盖新的规则“ )。如果匹配的期望不能再接受更多的调用时,你就会收到一个超出上界的失败,下面是一个例子:

using ::testing::_;...

EXPECT_CALL(turtle, Forward(_));  // #1

EXPECT_CALL(turtle, Forward(10))  // #2

.Times(2);

如果Forward(10)被连续调用3次,第3次调用它会报出一个错误,因为最后一个匹配期望(#2)已经饱和了。但是如果第3次的Forward(10)替换为Forward(20),那它就不会报错,因数现在#1将会是匹配的期望了。

边注:为什么Google Mock会以逆序去匹配期望呢?原因是为了可以让用户开始时使用Mock对象的默认行为,或是一些比较松驰的匹配条件,然后写一些更明确的期望。所以,如果你在同一个函数上有两个期望,你当然是想先匹配更明确的期望,然后再匹配其它的,或是可以说明确的规则会隐藏更宽泛的规则。

Ordered vs Unordered Calls

默认情况下,即使是在前一个期望没有被匹配的情况下,一个期望仍然可以被匹配。换句话说,调用的匹配顺序不会按照期望指定的顺序去匹配。

有时,你可能想让所有的期望调用都以一个严格的顺序来匹配,这在Google Mock中是很容易的:

using ::testing::InSequence;...

TEST(FooTest, DrawsLineSegment) {

...

{

InSequence dummy;

EXPECT_CALL(turtle, PenDown());

EXPECT_CALL(turtle, Forward(100));

EXPECT_CALL(turtle, PenUp());

}

Foo();

}

创建InSequence的一个对象后,在这个对象作用域中的期望都会以顺序存放,并要求调用以这个顺序匹配。因为我们只是依赖这个对象的构造函数和析构函数来完成任务,所以对象的名字并不重要。

( 如果你只是关心某些调用的相对顺序,而不是所有调用的顺序?可以指定一个任意的相对顺序吗?答案是...可以!如果你比较心急,你可以在CookBook中找到相关的细节。)

All Expectations Are Sticky (Unless Said Otherwise)

现在让我们做一个小测验,看你掌握Mock到什么程度了。你如何测试turtle恰好经过原点两次?

当你想出你的解法之后,看一下我们的答案比较一下( 先自己想,别作弊 )。

using ::testing::_;...

EXPECT_CALL(turtle, GoTo(_, _))  // #1

.Times(AnyNumber());

EXPECT_CALL(turtle, GoTo(0, 0))  // #2

.Times(2);

假设turtle.GoTo(0,0)被调用了3次。在第3次,Google Mock会找到参数匹配期望#2。因为我们想要的是恰好经过原点两次,所以Google Mock会立即报告一个错误。上面的内容其实就是我们在“Using Multiple Expectations”中说过的。

上面的例子说明了Google Mock中默认情况下期望是严格的,即是指期望在达到它们指定的调用次数上界后仍然是有效的。这是一个很重要的规则,因为它影响着指定的意义,而且这种规则与许多别的Mock框架中是不一样的( 我们为什么会设计的不一样?因为我们认为我们的规则会使一般的用例更容易表达和理解 )。

简单?让我看一下你是不是真懂了:下面的代码是什么意思:

using ::testing::Return;

...

for (int i = n; i > 0; i--) {

EXPECT_CALL(turtle, GetX())

.WillOnce(Return(10*i));

}

如果你认为turtle.GetX()会被调用n次,并依次返回10, 20, 30, ...,唉,你还是再想想吧!问题是,我们都说过了,期望是严格的。所以第2次turtle.GetX()被调用时,最后一个EXPECT_CALL()会被匹配,所以马上会引起“超出上界”的错误。上面的代码其实没什么用途。

一个正确表达turtle.GetX()返回10, 20, 30,...,的方法是明确地说明期望不是严格的。换句话说,在期望饱和之后就失效

using ::testing::Return;

...

for (int i = n; i > 0; i--) {

EXPECT_CALL(turtle, GetX())

.WillOnce(Return(10*i))

.RetiresOnSaturation();

}

并且,有一个更好的解决方法,在这个例子中,我们期望调用以特定顺序执行。因为顺序是一个重要的因素,我们应该用InSequence明确地表达出顺序:

using ::testing::InSequence;

using ::testing::Return;

...

{

InSequence s;

for (int i = 1; i <= n; i++) {

EXPECT_CALL(turtle, GetX())

.WillOnce(Return(10*i))

.RetiresOnSaturation();

}

}

顺便说一下,另一个期望可能严格的情况是当它在一个顺序中,当这个期望饱和后,它就自动失效,从而让下一个期望有效。

Uninteresting Calls

一个Mock对象可能有很多函数,但并不是所有的函数你都关心。比如,在一些测试中,你可能不关心GetX()和GetY()被调用多少次。

在Google Mock中,你如果不关心一个函数,很简单,你什么也不写就可以了。如果这个函数的调用发生了,你会看到测试输出一个警告,但它不会是一个失败。

What Now?

恭喜!你已经学习了足够的Google Mock的知识了,你可以开始使用它了。现在你也许想加入googlemock讨论组,并开始真正地用Google Mock开始写一些测试——它是很有意思的,嗨,这可能是会上瘾的,我可是警告过你了喔!

如果你想提高你的Mock等级,你可以移步至CookBook。你可以在那学习更多的Google Mock高级特性——并提高你的幸福指数和测试快乐级别。

Copyright notice

所有的内容全部翻译自 Google 的文档 Google C++ Mocking Framework for Dummies , Koala++/ 屈伟

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

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

    Google C++ Mocking Framework for Dummies Google Mock启蒙篇 Version: 0.07< xmlnamespace prefix =" ...

  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. 在jmeter测试中模拟不同的带宽环境
  2. 警惕由于使用YYYY-MM-dd引发的一场生产问题
  3. 电脑网页打不开但qq能上解决方法
  4. 数字图像处理:图像的频域
  5. Linux学习之diction的编译与使用
  6. Java实现数据批量导入数据库(优化速度-2种方法)
  7. qt QMessageBox 中文乱码的问题
  8. 小区居民投诉要拆除通信基站 三大运营商联手“封杀”...
  9. 设计算法之分治法(补充)
  10. linux php yii安装教程,Linux 安装php 5.4 – yii2 环境
  11. 《重回耶路撒冷——犹太人的三千年》(Return to Jerusalem)读后感
  12. iPad,下载迅雷电影,迅雷HD出现“应版权方要求,文件无法下载”解决方法!
  13. linux系统可以安装企业微信,Linux 上安装微信客户端
  14. 硬盘属于计算机的主存吗,.硬盘装在主机箱内,因此硬盘属于主存对吗
  15. 绝对地址、相对地址、/、./、../之间的区别
  16. 我爱看的书-2021-4-17
  17. PHP-swoole 聊天室
  18. ocr识别身份证护照阅读器
  19. R语言进入,提示都是日文怎么切换
  20. 我是怎么学英语的(四级没过如何突破听说读写)

热门文章

  1. 给自己一个微笑,让生命带给我们更多的幸福。
  2. 数据结构-线性表(栈与队列的特殊性)
  3. 2020CCPC(威海) - Rencontre(树形dp)
  4. CodeForces - 1220D Alex and Julian(思维+数论)
  5. HDU - 4547 CD操作(LCA模板)
  6. 倾斜模型精细化处理_万能三维模型修复神奇——模方
  7. keil 查看 stm32 io波形_如何系统地入门学习stm32?
  8. 基于TCP的Socket通讯
  9. Vue实现Todo List
  10. Django开发准则与最佳实践