背景
GMock
咱們項目中如今的模塊測試框架使用了CATCH+GMock的方式實現迴歸測試和打樁。框架
GMock的介紹在官網上有,這裏爲了鋪墊,大概地描述一下GMock能實現的效果。大約能夠當作這樣:函數
- void A() {
- if(B()) {
- //...
- }
- Else{
- //...
- }
-
- }
A是被測函數,B是樁函數。測試
在測試的,使用GMock的話,咱們能夠這樣寫測試代碼:spa
- TEST_CASE(Test As normal case) {
-
- EXPECT_CALL(mockobj, B).times(1).WillOnce(Return(true)); // MockBAtrue
-
- A(); // A
- // BFailedB should be called but not called
-
- }
模塊測試
因此,使用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):作用域
- TEST_START()
-
- SendMsg(toB, msg1);
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
-
- SendMsg(toB, msg2);
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
-
- TEST_END()
可是,因爲GMock的使用方法決定,咱們必須先寫成這樣:it
- TEST_START()
-
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg1);
-
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg2);
-
- TEST_END()
在很長的時序和不少的樁的狀況下這就顯得很彆扭了。編寫和維護的時候都很容易出錯。
問題
能不能提供一種辦法(宏),使得咱們能夠像(Program1)那樣的順序寫代碼,
同時,代碼又是以Program2這樣的順序來執行呢?(即,書寫時按咱們的正常思路寫,執行時,按GMock須要的順序執行)
好比:寫代碼時能夠這樣:
- TEST_START()
-
- TEST_STEP(SendMsg(toB, msg1))
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
-
- TEST_STEP(SendMsg(toB, msg2))
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
-
- TEST_END()
而實際的執行順序是:
- IF1_isExpectedTobeCalled(Mock)
- IF2_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg1);
-
- IF3_isExpectedTobeCalled(Mock)
- IF4_isExpectedTobeCalled(Mock)
- SendMsg(toB, msg2);
解法
中間我本身的折騰過程總不詳細描述了,實際上咱們就是要實現推調用的效果,並且,因爲咱們知道調用須要推遲到哪一個點,那麼很是容易想到「析構函數」,由於析構函數會在做用域結束時被調用。因此咱們若是能夠把函數調用存儲在一個對象裏,而後讓這個對象在指定的點析構,析構時調用咱們以前存儲的函數,目的就達到了。問題是「函數」如何存儲。答案就是C++11中提供的function庫和lamabda表達式,實現方法以下:
- class CallLater {
- public:
- CallLater(function<void(void)> _fun): m_fun(_fun){
-
- }
-
- ~CallLater() {
- m_fun();
- }
- private:
- function<void(void)> m_fun;
- };
-
-
- #define TEST_STEP(fun) } { CallLater temp ([](){ fun; });
- #define TEST_START() {
- #define TEST_END() }
至關地簡潔和舒服。這就是爲何我很是喜歡C++11中的那些「語法糖」。