背景

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. 实验一报告 20135238&20135207

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础              班级:1352 姓名:(按贡献大小排名)龚睿  王国伊 学号:(按贡献大小排 ...

  2. 高版本jquery尤其是1.10.2的版本设置input radio设置值的最正确的姿势。

    $("input:radio[name="analyshowtype"]").attr("checked",false); $(" ...

  3. 分享一个JavaScript嗅探工具Tracker

    今天发现一个好用的JS侦测工具,实时监测JS代码的使用效率,运行状态等. 期待插件作者添加更好的功能. 运行效果 运行效果如下(我把它放到标签里了): 1.点击Tracker书签工具 2.选择查看选项 ...

  4. Docker私有仓库Registry的搭建验证

    1. 关于Registry 官方的Docker hub是一个用于管理公共镜像的好地方,我们可以在上面找到我们想要的镜像,也可以把我们自己的镜像推送上去.但是,有时候,我们的使用场景需要我们拥有一个私有 ...

  5. SSRS开发的经验记录

    虽然工作经验相当的长,但是之前在SSRS上还没有象今天这样的经验.这只是工作经验的一点记录. 1. 定义DataSet 定义DataSet的时后,可以采用Text的方式.用Text的方式可以用一段比较 ...

  6. Linux各目录作用

  7. HOW TO REMOTELY DEBUG APPLICATION RUNNING ON TOMCAT FROM WITHIN INTELLIJ IDEA

    This post would look into how to tackle and debug issues in scenarios where they only occur in produ ...

  8. Chrome的Crash Report服务

    <本文转自:http://www.cppblog.com/woaidongmao/archive/2009/10/22/99211.aspx> 本文翻译自debugInfo网站上一篇文章g ...

  9. [转]Oracle SOME,ANY,All,EXISTS,IN

    原文地址:http://blog.csdn.net/shangboerds/article/details/43983791 -- Start 这几个关键字有一个共同点,那就是它们一般应用于子查询中. ...

  10. WPF--调用线程必须为 STA,因为许多 UI 组件都需要(转载)

    自VS2005开始,UI元素在不同线程中访问就受到限制了,当然你也可以解除这种限制 以下提供Framework3.0的解决方案发: public partial class Window1 : Win ...