CPPUnit code brief | Part 1, core class (transfer)

xiaoxiao2021-03-06  42

In a sense, CPPUnit's code is not a good C code. Because it is not a very good C code, and the amount of code is not very large (the main library 80K), so I feel more suitable for people who want to use CPPUnit and need to know in-depth understanding of C , want to read some simple source code. / Library people.

This article is not suitable for people who have never used CPPUnit, if you have never used CPPUnit, but you are very interested in test driver development, you can refer to my article: CPPUnit Getting Started.

I. Purpose

A new engine was taken before, and I found CPPUnit to do unit testing, so small studies, in the case of writing the experience of reading CPPUnit code, sharing with you. In a sense, CPPUnit's code is not a good C code, which is mainly caused by historical reasons: first cppunit is a transplant to Junit, so many places are used as Java, from style to Seminges, is Java; of course, this is not very thick, because for transplants, as long as they can be used as they originally used, others are second. Second, CPPUNIT (excessive?) Considering compatibility, in order to use it under different C compilers and standard libraries, it has set a lot of coding standards to yourself, such as "Don't use mutable", "Don't use Typename" And even "Do not use the AT members in the STL container", .... Because it is not a very good C code, and the amount of code is not very large (the main library 80K), so I feel more suitable for people who want to use CPPUnit and need to know in-depth understanding of C , want to read some simple source code. / Library (if you have experience, I want to learn the latest C technology, or want to read large libraries, then you can read other open source libraries.)

This article is not suitable for people who have never used CPPUnit, if you have never used CPPUnit, but you are very interested in test driver development, you can refer to my article: CPPUnit Getting Started.

Ii. Code

CPPUnit is a UnitTest framework, which is the main mechanism that allows you to write test cases for each code, and manage a lot of test cases to manage and provide you with different interfaces and output. So I think its code can also be divided into corresponding parts: 1. Test case and its management, this part is only managed and test-related data structures, and they are often static initialization, and they have not been in front of the real test. It has been fully implemented. 2. The code that actually tests, this part contains the initialization of the test, the exception handling in the test, and the notification of test processes and results. This part of the code is the code that is executed in your test run. 3. The interface and input of the test. For example, CPPUnit provides an MFC-based test interface that you can select some test cases for testing and get results. This part of the code will not be described in detail, because the test itself is not big.

III. Test case and its management

This part of the class has TEST, TESTLEAF, TESTCASE, TESTComposite, and TestSuite and some auxiliary classes like TestPath. This part of the code is actually unconditional with UnitTest itself, it is just an object tree management. CPPUnit manages all objects that can be used as a tree in the test system, each test is a node on the tree, which is parental class with TEST. TestPath

If the entire test case tree is used as a file tree, TestPath represents a relative / absolute path. It is saved inside, which means that it turns a queue that is similar to / root / suite / case or / suite / child / case to a Test object passed on a path. Through this structure, I can easily move at the nodes in the tree structure.

2. Test

In the TEST class, in addition to the false argument function and Virtual Void Run (TestResult * Result) = 0 This pure virtual function is used to perform tests, other functions are used to do tree management. Because the tree is a recursive definition, as a node, it only needs to enumerate your own child node. These related functions are: countTestCases to return this node and how many effective TEST objects containing the nodes in their subtitches, which is mainly the purpose of counting, because many nodes on the TEST tree do not contain any test code; getChildTestCount Returns the number of direct child nodes; getChildTestat accepts an index and returns the corresponding child node; getName returns the name of this node. The above functions are pure virtual functions, and the specific definitions are given by subclasses. getChildtest is a virtual function that actually calls CheckisvalidIndex and DogetChildTestat, and CPPUnit is also suggesting that you don't overload it, but overload the DogetChildTestat function. This is the Template Method mode in Gof, but since it is not recommended here, you should not use the Virtual function. Or and may be a shadow of junit. The Test class truly implemented function is to process the non-direct child node. As previously mentioned, for the direct sub-node can be represented by an index, and for the non-direct sub-node is represented by TestPath. Test provides FindTestPath, FindTest, and ResolvetestPath functions to support these features. Although these functions are presented in the form of virtual functions, that is, theoretical can be expanded, but in fact, only TEST classes are implemented throughout the CPPUnit system.

3. Testleaf

Testleaf is a subclass of Test, which represents a leaf node on the TEST tree. Its implementation is simple, returns 1 for countTestCases, because it is itself a valid Test; getChildTestCount returns 0, because there is no child node; its dogetchildTestat is not called at all, it should not be called.

4. Testcomposite

TestComposite is also a subclass of TEST, which represents a non-root node on the Test tree, where it is similar to a GOF composite mode. Its countTestCases returns the accumulation of countTestCases for all child nodes. From this point, TestComposite itself is just a TEST container, not as an effective Test. The TestComposite Run function calls its own DostartSuite function, in this function, the TestResult's StartSuite is called; then the RUN function is then done through the DorunchildTests function to indirect all child nodes of Run, and finally call the TestResult's EndSuite function through the Doendsuite function. TestComposite's RUN function is actually a Template Method mode because it is usually customized by these three DO **** functions. The call to Testrunner :: StartSuite / Endsuite is to make the incoming Testrunner get notified before and after each Suite is tested to do some bookkeeping work. (Of course, the name here is best called StartComposite / endcomposite.) 5. Testcase

TestCase inherits from Testleaf and TestFixTure, which is one of the most commonly used categories in the entire CPPUnit. As the name suggests, TestCase represents the test cases referred to in the usual UnitTest, and is also the most commonly used leaf node in the entire TEST tree. This class has its location in the TEST tree, also directly involved in the test, so it is placed in the next section.

6. Testsuite

TestSuite is inherited from TestComposite, TestComposite provides operational mechanisms for those non-root nodes, while TestSuite provides the management of child nodes on this basis. For example, with the addTest function to add TEST, DeleteContents delete all sub-Test objects, which also implements the number and pointers of the getChildTestCountCount and DogetChildTestat to return the subtocks.

IV. Practical test code

These classes include TestFixTure, Testcase, TestCaller, Testrunner, TestListener, TestResult, and Protector system.

1. TestFixTure and it is said that the Test class is the root of all TEST, but TestFixTure is the root of all "tests" in CPPUnit. Because the class from the Test class is just the "management department" of this system, the class from TestFixTure inherits the "executive department" that truly test. In addition to a false preframe function ("Please derive" in C , TestFixTure defines two virtual functions: setup and Teardown, the former initializes a test, the latter clears all the side effects generated by the test. The definitions of these three functions in TestFixTure are empty.

2. Testcase This is our second time to see it, because it is in the intersection of two types, inherits from Testleaf and TestFixTure. This class does not neither the Testleaf to be enhanced as a Test Tree node, nor does it redefine setup and Teardown in TestFixTure. The only thing to do is defined Run. Its Run will call Testrunner :: Startcase, then call Setup, then call the new defined RunTest function in TestCase, then call TEARDOWN and TESTRunner :: endcase. From the comment, you can see that the author wants to define the Runtest as a pure virtual function, that is, actually derived from TestCase and defines a simple / single test work. If you want to perform a complex test work / build complex test case tree, you should use other mechanisms. There are two reasons why: First, from the management point of view, if you have several test tasks, you should use them as different nodes in the Test tree so that you can count the total number of tasks / failures, and TestCase is A Testleaf, if you put them in a TestCase, it is not conducive to management; second, if you have a few test tasks to do, and share the same initialization / clear code, then you want to derive this from Testcase to do this. You must override Setup, Teardown, and Run. Rewrite the first two, if Run has rewritten, this, why also derive it from Testcase? 3. TestCallertestCaller is an Adapter mode in a GoF that can be packaged for any object that defines the class of Setup / Teardown as a TestCase, and calls a function of this object in Runtest. That is, if I have a TestFixTure's derived Foobartest, there is a Footest and a Bartest function, then TestCaller (foobartest :: footest) and testcaller (foobartest :: battest) is two Testcase derived Class, their instances can be added as TestCase to the TEST tree, or can be used independently. TestCaller is derived from TestCase and accepts an instance of a class (you can also generate one by New to generate one) and the pointer's pointer as constructor parameters and saved in the SETUP and TEARDOWN functions, it calls the object. Setup and Teardown, and use saved objects in the RunTest function to call its specified member function. The advantage of using TestCaller is that you can put a set of Test tasks in a class derived from TestFixTure, and package them into several Testcase with TestCaller. This makes it easy for the management of the relevant test tasks, and two can make different tasks into different sub-nodes of the TEST tree.

4. TestListenertestListener is actually a receiver for testing events that define StartTest, EndTest, StartSuite, EndSuite, StartTestrun, EndStrun, and AddFail:, you can define your own derived class and bought yourself The event is processed.

5. TestResultTestResult from SynchronizedObject inherited, SynchronizedObject in fact, for the Java synchronized analog, SynchronizedObject and SynchronizedObject :: ExclusiveZone is an example of a typical use RAII to exclusive access to a particular scope. TestResult defines the AddListener and Removelistener method to manage the event's subscriber, and it also defines all Listener methods. When you call a TestResult, it will send this event to all Listener, but cppunit Developers don't think TestResult is a TestListener, so even if it achieves all functions of TestListener even in the same way, there is no inheritance from TestListener. TestResult maintains whether a test process is forcibly aborted, and manages it via RESET, STOP and SHOULDStop, which gives the testing of the test a response to an opportunity for ambaling. TestResult adds several functions, and Runtest is one of them, which receives a TEST pointer as a parameter and calls the RUN of this Test object. When you have a Test object and a TestResult object, you can complete a test task through Test :: Run (TestResult *) or TestResult :: RunTest (Test *), which is different from the latter call Test :: Run TestResult :: StartTestrun and TestResult :: endTestrun are called before and after. Other interesting functions are Protect, PushProtector and Popprotector. These three functions actually maintain a ProtectorChain object and call protectorchain :: Protect in Protect to provide a protected environment for testing. 6. The Protector class system knows that when we run a test task, you can judge the success according to the return value or fail, but some "bad" function will throw an exception, we must capture an exception, otherwise you can't enter A test will end in advance in advance. Basic Protector only provides some an error-free auxiliary functions. By viewing the code of the defaultprotector :: Protect, you can know that in its Protect, you will try to capture Exception and Std :: Exception and capture all unknown exceptions. DefaultProtector is suitable as a "final selection" to use because we want to know if we throw some or more specific exceptions, which requires ProtectorChain. ProtectorChain :: Protect has such a code:

Functors functors; for (int index = m_protectors.size () - 1; index> = 0; --index) {const Functor & protectedFunctor = functors.empty () functor:? * Functors.back (); functors.push_back (new ProtectFunctor (M_Protectors [index], protectedfunctor, context);}

In this code, M_Protectors is a set of Protector, which connects these Protector through ProtectFunctor, and its root is functor, which is the function that needs to be protected. Finally, when the last element of this Functors is called, it first establishes its own protection mechanism, then calls its functor, the functor is its previous element, which may be another Protector, or the most primitive protected Protector, In this way, that is, all protection is nestled in one layer. V. Small knot

In fact, there are still many meaningful code in the CPPUnit, such as its test results output mechanism, its auxiliary macro and TestSuite global registration mechanism, etc., I will introduce in future articles.

转载请注明原文地址:https://www.9cbs.com/read-61372.html

New Post(0)