Gmock depth Analysis of C + + unit Test framework
With the popularity of MicroServices and CI, unit testing can be said to be an essential part in the field of software engineering, and unit testing has been improved to a new height in TDD. But many companies, for many different reasons, have not been able to maintain, or have never written unit tests, indeed, unit testing in the initial and code maintenance period will need to spend some investment, but if a project is required for long-term maintenance and update, then the role of unit testing, compared to the input is nothing. See a lot of people write unit test, although can also run, there is coverage, but a little analysis will see, it is not unit testing, and is already an integration test, such as someone should be in the unit test access to the network, write files, and even read and write the database.
So what kind of database is good unit test, according to the author's experience, the following points may be necessary:
1. Running fast, for a test with hundreds of unit test cases, I expect to be able to run within 1-2 minutes, if I am refactoring the code, this allows me to get feedback in a very short period of time.
2. Do not rely on external factors, unit testing only for single function functional testing
3. A use case tests only one function
For the 2nd, it may be troublesome, because if a function is a member function of a type, then it is very likely to rely on many internal member variables, which is the time for the mock to come out, because using a mock allows us to focus on the test of our function's business logic and isolate it. I have used a lot of language of the mock library, with the most handy or Java Mokito, of course, C + + language has a lot of similar products, such as gmock, fake it, but its limitations are really more, if not at the beginning of the code to understand, and do plan, later want to join unit testing , and the use of Gmock may be a regret, a fuss, let's take a look at the situation below to analyze these limitations.
Scenario 1:
classTurtlereal { Public: voidPenup () {}voidPendown () {}};classMockturtlereal: PublicTurtlereal { Public: Mock_method0 (Penup,void()); MOCK_METHOD0 (Pendown,void());};classpainterdreal{turtlereal*Turtle; Public: Painterdreal (turtlereal*turtle): Turtle (Turtle) {}BOOLDrawcircle (int,int,int) {Turtle-Pendown (); return true; }}; TEST (Paintertest, childrealcandrawsomething) {mockturtlereal turtle; Expect_call (Turtle, Pendown ()). Times (AtLeast (1)); painterdreal painter (&turtle); Expect_true (painter. Drawcircle (0,0,Ten));}
Result 1:
Conclusion One:
Why use a regular failure, gmock rely on C + + polymorphic mechanism to work, only virtual function can be mock, non-virtual function can not be mock, this point tells us, if you want to use the Gmock class in the code design, preferably using interface isolation, for C + + is also a pure virtual type, Because C + + itself does not have an interface type.
Scenario 2:
classTurtle { Public: Virtual~Turtle () {}Virtual voidPenup () =0; Virtual voidPendown () =0;};classMockturtle: PublicTurtle { Public: Mock_method0 (Penup,void()); MOCK_METHOD0 (Pendown,void());};classpainter{Turtle*Turtle; Public: Painter (Turtle*turtle): Turtle (Turtle) {}BOOLDrawcircle (int,int,int) {Turtle-Pendown (); return true; }}; TEST (Paintertest, candrawsomething) {Mockturtle turtle; Expect_call (Turtle, Pendown ()). Times (AtLeast (1)); Painter Painter (&turtle); Expect_true (painter. Drawcircle (0,0,Ten));}
Result 2:
Conclusion Two:
To change a function to a virtual function, the test case passes
Scenario 3:
classTurtlechild:turtle { Public: voidPenup () {intA =0; }; voidPendown () {intb =0; };};classMockturtlechild: PublicTurtlechild { Public: Mock_method0 (Penup,void()); MOCK_METHOD0 (Pendown,void());};classpainterchildref{turtlechild Turtle; Public: Painterchildref (Turtlechild&turtle): Turtle (Turtle) {}BOOLDrawcircle (int,int,int) {Turtle. Pendown (); return true; }}; TEST (Paintertest, childcandrawsomething) {Mockturtlechild turtle; Expect_call (Turtle, Pendown ()). Times (AtLeast (1)); painterchild painter (&turtle); Expect_true (painter. Drawcircle (0,0,Ten));}
Result 3:
Conclusion Three:
The test case passes, the function with the same name in the derived class is still a virtual function, also supports polymorphism, supports GOMCK
Scenario 4:
classTurtle { Public: Virtual~Turtle () {}Virtual voidPenup () =0; Virtual voidPendown () =0;};classTurtlechild:turtle { Public: voidPenup () {intA =0; }; voidPendown () {intb =0; };};
Class Mockturtlechild:public Turtlechild {
Public
MOCK_METHOD0 (Penup, Void ());
MOCK_METHOD0 (Pendown, Void ());
};
classpainterchildref{turtlechild Turtle; Public: Painterchildref (Turtlechild&turtle): Turtle (Turtle) {}BOOLDrawcircle (int,int,int) {Turtle. Pendown (); return true; }}; TEST (Paintertest, childrefcandrawsomething) {Mockturtlechild turtle; Expect_call (Turtle, Pendown ()). Times (AtLeast (1)); painterchildref painter (turtle); Expect_true (painter. Drawcircle (0,0,Ten));}
Result 4:
Conclusion Four:
The test case fails, and the member variable passed in as a reference type does not have a polymorphic property on its own, so Gmock does not support
Conclusion
This article through four scenes, layers of progressive, in-depth analysis of the use of Gmock, I hope you write the code early to do the plan to avoid the fuss, rework again. But from another aspect, the interface isolation, P-impl idioms and other technologies, should be a C + + veteran of the necessary magic weapon, can see a lot of things have its reason, the early not understand, later only to spend more energy to make up, or to overthrow the reconstruction, or directly give up, ignorant fearless, no zuo, no die ..