背景

GMock

我们项目中现在的模块测试框架使用了CATCH+GMock的方式实现回归测试和打桩。

GMock的介绍在官网上有,这里为了铺垫,大概地描述一下GMock能实现的效果。大约可以看成这样:

  1. void A() {
  2.     if(B()) {
  3.         //...
  4.     }
  5.     Else{
  6.         //...
  7.     }
  8. }

A是被测函数,B是桩函数。

在测试的,使用GMock的话,我们可以这样写测试代码:

  1. TEST_CASE(Test As normal case) {
  2.     EXPECT_CALL(mockobj, B).times(1).WillOnce(Return(true)); // MockBAtrue
  3.     A(); // A
  4.     // BFailedB should be called but not called
  5. }

模块测试

所以,使用GMock以后我们可以很愉快地打桩了,但是有一个问题是,必须在调用被测函数 (A)之前给B函数打桩(描述B应该被调用几次,以及有什么样的行为)。这在UT中虽然是没有什么问题的(因为UT中函数只调用一次),但是要是用在模块的时序测试上,就会使人产生时序上的混乱感。

比如我们有一个时序:

Tester  ---Msg1-–> B
                         B call IF1
                         B call IF2

Tester  ---Msg2-–> B
                         B call IF3
                         B call IF4

我们如果正常地按时序思路写测试代码,那么希望是这样的(Program1):

  1. TEST_START()
  2. SendMsg(toB, msg1);
  3. IF1_isExpectedTobeCalled(Mock)
  4. IF2_isExpectedTobeCalled(Mock)
  5. SendMsg(toB, msg2);
  6. IF3_isExpectedTobeCalled(Mock)
  7. IF4_isExpectedTobeCalled(Mock)
  8. TEST_END()

但是,由于GMock的使用方法决定,我们必须先写成这样:

  1. TEST_START()
  2. IF1_isExpectedTobeCalled(Mock)
  3. IF2_isExpectedTobeCalled(Mock)
  4. SendMsg(toB, msg1);
  5. IF3_isExpectedTobeCalled(Mock)
  6. IF4_isExpectedTobeCalled(Mock)
  7. SendMsg(toB, msg2);
  8. TEST_END()

在很长的时序和很多的桩的情况下这就显得很别扭了。编写和维护的时候都很容易出错。

问题

能不能提供一种办法(宏),使得我们可以像(Program1)那样的顺序写代码,

同时,代码又是以Program2这样的顺序来执行呢?(即,书写时按我们的正常思路写,执行时,按GMock需要的顺序执行)

比如:写代码时可以这样:

  1. TEST_START()
  2. TEST_STEP(SendMsg(toB, msg1))
  3. IF1_isExpectedTobeCalled(Mock)
  4. IF2_isExpectedTobeCalled(Mock)
  5. TEST_STEP(SendMsg(toB, msg2))
  6. IF3_isExpectedTobeCalled(Mock)
  7. IF4_isExpectedTobeCalled(Mock)
  8. TEST_END()

而实际的执行顺序是:

  1. IF1_isExpectedTobeCalled(Mock)
  2. IF2_isExpectedTobeCalled(Mock)
  3. SendMsg(toB, msg1);
  4. IF3_isExpectedTobeCalled(Mock)
  5. IF4_isExpectedTobeCalled(Mock)
  6. SendMsg(toB, msg2);

 

解法

中间我自己的折腾过程总不详细描述了,实际上我们就是要实现推调用的效果,而且,由于我们知道调用需要推迟到哪个点,那么非常容易想到“析构函数”,因为析构函数会在作用域结束时被调用。所以我们如果可以把函数调用存储在一个对象里,然后让这个对象在指定的点析构,析构时调用我们之前存储的函数,目的就达到了。问题是“函数”如何存储。答案就是C++11中提供的function库和lamabda表达式,实现方法如下:

  1. class CallLater {
  2. public:
  3.     CallLater(function<void(void)> _fun): m_fun(_fun){
  4.     }
  5.     ~CallLater() {
  6.         m_fun();
  7.     }
  8. private:
  9.     function<void(void)> m_fun;
  10. };
  11. #define TEST_STEP(fun)  } { CallLater temp ([](){ fun; });
  12. #define TEST_START()     {
  13. #define TEST_END()       }

相当地简洁和舒服。这就是为什么我非常喜欢C++11中的那些“语法糖”。

推迟调用以及Lambda表达式的更多相关文章

  1. 深入探究JVM之方法调用及Lambda表达式实现原理

    @ 目录 前言 正文 解析 分派 静态分派 动态分派 单分派和多分派 动态分派的实现 Lambda表达式的实现原理 MethodHandle 总结 前言 在最开始讲解JVM内存结构的时候有简单分析过方 ...

  2. 反射调用与Lambda表达式调用

    想调用一个方法很容易,直接代码调用就行,这人人都会.其次呢,还可以使用反射.不过通过反射调用的性能会远远低于直接调用——至少从绝对时间上来看的确是这样.虽然这是个众所周知的现象,我们还是来写个程序来验 ...

  3. 深入探索Java 8 Lambda表达式

    2014年3月,Java 8发布,Lambda表达式作为一项重要的特性随之而来.或许现在你已经在使用Lambda表达式来书写简洁灵活的代码.比如,你可以使用Lambda表达式和新增的流相关的API,完 ...

  4. Lambda 表达式的示例-来源(MSDN)

    本文演示如何在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述,请参阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的详细信息,请参阅 Lambda 表 ...

  5. Lambda 表达式的演示样例-来源(MSDN)

    本文演示怎样在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述.请參阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的具体信息,请參阅 Lambda 表 ...

  6. Lambda表达式的本质是匿名函数

    1.委托的简介: 委托可以简单的理解为方法的列表,添加的方法的参数类型,个数,顺序必须和委托一致, 也就是说委托起到了托管方法的作用,并且约束了要调用的方法. //1声明委托 public deleg ...

  7. C++11新特性(3) lambda表达式(1)

    C++11加入了一项名为lambda表达式的新功能.通过这项功能能编写内嵌的匿名函数,而不必编写独立函数或函数对象,使得代码更加理解. lambda表达式包括下面部分. [capture_block] ...

  8. lambda表达式&map&filter&yield

    一.先来看下lambda表达式 1.lambda表达式其实很简单,他是简单的函数的变种,只有三部分组成,之前老师没有讲清楚,今天看书,终于明白了,写个博客记录下 lambda关键字+参数+返回值,参数 ...

  9. C11简洁之道:lambda表达式

    1.  定义 lambda表达式是C++11非常重要也是很常用的特性之一,来源于函数式编程的概念,也是现代编程语言的一个特点.它有如下特点: 声明式编程风格:就地匿名定义目标函数或者函数,不需要额外写 ...

随机推荐

  1. 阿里客户端工程师试题简析——Android应用的闪退(crash)分析

    1. 问题描述 闪退(Crash)是客户端程序在运行时遭遇无法处理的异常或错误时而退出应用程序的表现,请从crash发生的原因分类与解决方法.在出现crash后如何捕捉并分析异常这两个问题给出自己的解 ...

  2. 快速替换dll命名空间

    时15年9月18日,闲来无事,更一博.  背景 三天前,Y公司为避免法律诉讼,需要将代码(包括dll)中有关老东家的命名空间全部改掉.现在我就将快速替换命名空间的方法一步步告诉大家,注意,此举不是为了 ...

  3. 【开源】XPShadow, 用阴影让UWP更有层次感

    UWP采用的是纯扁平化的设计,个人感觉极端了点,整个世界都是平的,导致App分不清层次,看不出重点.其实扁平化是趋势,android, ios都在搞,问题是android, ios都可以很轻松的实现阴 ...

  4. [原创]Net实现Excel导入导出到数据库(附源码)

    关于数据库导出到Excel和SQLServer数据导出到Excel的例子,在博客园有很多的例子,自己根据网上搜集资料,自己做了亦歌简单的demo,现在分享出来供初学者学习交流使用. 一.数据库导入导出 ...

  5. 【BZOJ1013】【JSOI2008】球形空间产生器sphere(高斯消元)

    1013: [JSOI2008]球形空间产生器sphere Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1600  Solved: 860[Submi ...

  6. KMP和扩展KMP

    文章网上太多这里提一下代码细节: KMP: scanf("%s\n",s); scanf("%s\n",t); int ls=strlen(s),lt=strl ...

  7. 传智168期JavaEE就业班 day02-css

    * 课程回顾: * HTML语言 * HTML的简介 超文本标记语言. * 是网页最基础的语言. * 都是由标签所组成的. * HTML的基本格式 <html> <head> ...

  8. 更新java对xml文件的操作

    //更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...

  9. JS中对象与字符串的互相转换

    在使用 JSON2.JS 文件的 JSON.parse(data) 方法时候,碰到了问题: throw new SyntaxError('JSON.parse'); 查询资料,大概意思如下: JSON ...

  10. css中的默认margin

    上班打酱油中,你懂的; body的margin为8px; webkit默认行高18px:height18px; 默认font-size16px p默认margin是16px 0 16px 0; ul和 ...