推迟调用以及Lambda表达式
背景
GMock
我们项目中现在的模块测试框架使用了CATCH+GMock的方式实现回归测试和打桩。
GMock的介绍在官网上有,这里为了铺垫,大概地描述一下GMock能实现的效果。大约可以看成这样:
- void A() {
- if(B()) {
- //...
- }
- Else{
- //...
- }
- }
A是被测函数,B是桩函数。
在测试的,使用GMock的话,我们可以这样写测试代码:
- TEST_CASE(Test As normal case) {
- EXPECT_CALL(mockobj, B).times(1).WillOnce(Return(true)); // MockBAtrue
- A(); // A
- // BFailedB should be called but not called
- }
模块测试
所以,使用GMock以后我们可以很愉快地打桩了,但是有一个问题是,必须在调用被测函数 (A)之前给B函数打桩(描述B应该被调用几次,以及有什么样的行为)。这在UT中虽然是没有什么问题的(因为UT中函数只调用一次),但是要是用在模块的时序测试上,就会使人产生时序上的混乱感。
比如我们有一个时序:
Tester ---Msg1-–> B
B call IF1
B call IF2Tester ---Msg2-–> B
B call IF3
B call IF4
我们如果正常地按时序思路写测试代码,那么希望是这样的(Program1):
- TEST_START()
- SendMsg(toB, msg1);
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg2);
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- TEST_END()
但是,由于GMock的使用方法决定,我们必须先写成这样:
- TEST_START()
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg1);
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg2);
- TEST_END()
在很长的时序和很多的桩的情况下这就显得很别扭了。编写和维护的时候都很容易出错。
问题
能不能提供一种办法(宏),使得我们可以像(Program1)那样的顺序写代码,
同时,代码又是以Program2这样的顺序来执行呢?(即,书写时按我们的正常思路写,执行时,按GMock需要的顺序执行)
比如:写代码时可以这样:
- TEST_START()
- TEST_STEP(SendMsg(toB, msg1))
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- TEST_STEP(SendMsg(toB, msg2))
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- TEST_END()
而实际的执行顺序是:
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg1);
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg2);
解法
中间我自己的折腾过程总不详细描述了,实际上我们就是要实现推调用的效果,而且,由于我们知道调用需要推迟到哪个点,那么非常容易想到“析构函数”,因为析构函数会在作用域结束时被调用。所以我们如果可以把函数调用存储在一个对象里,然后让这个对象在指定的点析构,析构时调用我们之前存储的函数,目的就达到了。问题是“函数”如何存储。答案就是C++11中提供的function库和lamabda表达式,实现方法如下:
- class CallLater {
- public:
- CallLater(function<void(void)> _fun): m_fun(_fun){
- }
- ~CallLater() {
- m_fun();
- }
- private:
- function<void(void)> m_fun;
- };
- #define TEST_STEP(fun) } { CallLater temp ([](){ fun; });
- #define TEST_START() {
- #define TEST_END() }
相当地简洁和舒服。这就是为什么我非常喜欢C++11中的那些“语法糖”。
推迟调用以及Lambda表达式的更多相关文章
- 深入探究JVM之方法调用及Lambda表达式实现原理
@ 目录 前言 正文 解析 分派 静态分派 动态分派 单分派和多分派 动态分派的实现 Lambda表达式的实现原理 MethodHandle 总结 前言 在最开始讲解JVM内存结构的时候有简单分析过方 ...
- 反射调用与Lambda表达式调用
想调用一个方法很容易,直接代码调用就行,这人人都会.其次呢,还可以使用反射.不过通过反射调用的性能会远远低于直接调用——至少从绝对时间上来看的确是这样.虽然这是个众所周知的现象,我们还是来写个程序来验 ...
- 深入探索Java 8 Lambda表达式
2014年3月,Java 8发布,Lambda表达式作为一项重要的特性随之而来.或许现在你已经在使用Lambda表达式来书写简洁灵活的代码.比如,你可以使用Lambda表达式和新增的流相关的API,完 ...
- Lambda 表达式的示例-来源(MSDN)
本文演示如何在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述,请参阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的详细信息,请参阅 Lambda 表 ...
- Lambda 表达式的演示样例-来源(MSDN)
本文演示怎样在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述.请參阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的具体信息,请參阅 Lambda 表 ...
- Lambda表达式的本质是匿名函数
1.委托的简介: 委托可以简单的理解为方法的列表,添加的方法的参数类型,个数,顺序必须和委托一致, 也就是说委托起到了托管方法的作用,并且约束了要调用的方法. //1声明委托 public deleg ...
- C++11新特性(3) lambda表达式(1)
C++11加入了一项名为lambda表达式的新功能.通过这项功能能编写内嵌的匿名函数,而不必编写独立函数或函数对象,使得代码更加理解. lambda表达式包括下面部分. [capture_block] ...
- lambda表达式&map&filter&yield
一.先来看下lambda表达式 1.lambda表达式其实很简单,他是简单的函数的变种,只有三部分组成,之前老师没有讲清楚,今天看书,终于明白了,写个博客记录下 lambda关键字+参数+返回值,参数 ...
- C11简洁之道:lambda表达式
1. 定义 lambda表达式是C++11非常重要也是很常用的特性之一,来源于函数式编程的概念,也是现代编程语言的一个特点.它有如下特点: 声明式编程风格:就地匿名定义目标函数或者函数,不需要额外写 ...
随机推荐
- [CareerCup] 12.5 Test a Pen 测试一支笔
12.5 How would you testa pen? 这道题让我们测试一支笔,我们需要问面试官许多问题来理解"who,what,where,when,how and why" ...
- Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载
目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...
- 高校手机签到系统——zxing.net生成二维码(补充)
高校手机签到系统——第一部分Authority权限系统(上) 高校手机签到系统——第一部分Authority权限系统(下) 高校手机签到系统——手机客户端 关于zxing.net的使用网上已有很多说明 ...
- 谈EntityFramework数据更新之技巧
EntityFramework是一个很不错的ORM框架,一直都在使用.今天想跟大家分享以下EntityFramework数据更新方面的几个技巧: 1:如何new一个新实体去更新记录,而不是从数据库中查 ...
- js验证身份证号
/* * 身份证检测(格式.地区.生日.年龄范围) * code:检测字符串 rangeAge:年龄范围[格式为:25-55] * 返回值 0:为空 ,不为0则验证不通过 */ : : : : : : ...
- js中的this中使用
请先查看:http://www.jb51.net/article/41656.htm 情况一:纯粹的函数调用 这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global. 情况二:作 ...
- [BZOJ2038]小Z的袜子(莫队算法)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2038 分析:莫队算法 莫队算法是一种思想…… 处理问题:不带修改的区间询问 使用要求:[l-1 ...
- [工具类]DataTable与泛型集合List互转
写在前面 工作中经常遇到datatable与list,对于datatable而言操作起来不太方便.所以有的时候还是非常希望通过泛型集合来进行操作的.所以这里就封装了一个扩展类.也方便使用. 类 方法中 ...
- DVR分布式路由
1. 背景 没有使用DVR的场景: 从图中可以明显看到东西向和南北向的流量会集中到网络节点,这会使网络节点成为瓶颈. 如果启用DVR,如下图: 对于东西向的流量, 流量会直接在计算节点之间传递. 对于 ...
- jquery操作滚动条滚动到指定位置
<html><head><script type="text/javascript" src="/jquery/jquery.js" ...