推迟调用以及Lambda表达式

背景

GMock

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

GMock的介绍在官网上有,这里为了铺垫,大概地描述一下GMock能实现的效果。大约能够当作这样:函数

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

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

在测试的,使用GMock的话,咱们能够这样写测试代码:spa

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

模块测试

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

好比咱们有一个时序:orm

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

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

咱们若是正常地按时序思路写测试代码,那么但愿是这样的(Program1):作用域

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

可是,因为GMock的使用方法决定,咱们必须先写成这样:it

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

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

问题

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

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

好比:写代码时能够这样:

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

而实际的执行顺序是:

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

 

解法

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

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

至关地简洁和舒服。这就是为何我很是喜欢C++11中的那些“语法糖”。

相关文章
相关标签/搜索