Background gmock
The current Module Testing Framework in our project uses the catch + gmock method to implement regression testing and piling.
The gmock introduction is available on the official website. Here, we will give a rough description of what gmock can achieve. It can be viewed as follows:
- Void (){
- ???? If (B ()){
- ???????? //...
- ????}
- ???? Else {
- ???????? //...
- ????}
-
- }
A is the tested function, and B is the pile function.
If gmock is used for testing, we can write the test code as follows:
- Test_case (test as normal case ){
-
- ???? Expect_call (mockobj, B). Times (1). willonce (Return (true); // mockbatrue
-
- ???? A (); //
- ???? // Bfailedb shocould be called but not called
-
- }
Module test
Therefore, after using gmock, we can easily pile up, but one problem is that we must call the function under test () call function B before (describe how many times B should be called and what kind of behavior B has ). Although there is no problem in Ut (because the UT function is called only once), if it is used in the timing test of the module, it will lead to confusion in the timing.
For example, we have a sequence:
Tester --- msg1 --> B
B call if1
B call if2
Tester --- msg2 --> B
B call if3
B call IF4
If we normally write the test code according to the time sequence, we hope this is the case (program1 ):
- Test_start ()
-
- Sendmsg (TOB, msg1 );
- If1_isexpectedtobecalled (Mock)
- If2_isexpectedtobecalled (Mock)
-
- Sendmsg (TOB, msg2 );
- If3_isexpectedtobecalled (Mock)
- If4_isexpectedtobecalled (Mock)
-
- Test_end ()
However, since the usage of gmock is determined, we must first write it as follows:
- Test_start ()
-
- If1_isexpectedtobecalled (Mock)
- If2_isexpectedtobecalled (Mock)
- Sendmsg (TOB, msg1 );
-
- If3_isexpectedtobecalled (Mock)
- If4_isexpectedtobecalled (Mock)
- Sendmsg (TOB, msg2 );
-
- Test_end ()
This is quite awkward with a long time series and a lot of piles. Errors may occur during writing and maintenance.
Problem
Can we provide a way (macro) to write code in sequence like program1,
At the same time, the code is executed in the order of program2? (That is, when writing, it is written according to our normal thinking, And when executing, it is executed in the order required by gmock)
For example, you can write code like this:
- 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 ()
The actual execution sequence is:
- If1_isexpectedtobecalled (Mock)
- If2_isexpectedtobecalled (Mock)
- Sendmsg (TOB, msg1 );
-
- If3_isexpectedtobecalled (Mock)
- If4_isexpectedtobecalled (Mock)
- Sendmsg (TOB, msg2 );
Solution
In the middle, my own tossing process is not described in detail. In fact, we want to implement the calling push effect. In addition, because we know the point at which the call needs to be postponed, it is very easy to think of the "destructor", because the Destructor will be called at the end of the scope. Therefore, if we can store function calls in an object, and then let this object call the previously stored function at the specified point structure, the goal is achieved. The question is how to store "functions. The answer is the function library and lamabda expression provided in C ++ 11. The implementation method is as follows:
- 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 ()???????}
It is quite concise and comfortable. That's why I like the "syntactic sugar" in C ++ 11 very much ".
Deferred call and lambda expressions