zoukankan      html  css  js  c++  java
  • 推迟调用以及Lambda表达式

    背景

    GMock

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

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

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

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

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

    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中函数只调用一次),但是要是用在模块的时序测试上,就会使人产生时序上的混乱感。

    比如我们有一个时序:

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

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

    我们如果正常地按时序思路写测试代码,那么希望是这样的(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的使用方法决定,我们必须先写成这样:

    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中的那些“语法糖”。

  • 相关阅读:
    21 情态动词
    20 动词的用法
    19 完成时/现在完成时和过去完成时的区别
    18 将来时
    17 一般过去时和过去进行时
    16 一般现在时和现在进行时
    15 There / Here be句型
    14 不定量表达法
    13 副词
    12 形容词
  • 原文地址:https://www.cnblogs.com/muxue/p/4067371.html
Copyright © 2011-2022 走看看