推遲調用以及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中的那些「語法糖」。

相關文章
相關標籤/搜索