This is a classic article that comprehensively introduces Unit testing. It is very helpful for understanding Unit testing and Visual Unit. The author has made a few changes during the recording.
Unit 1 test Overview
The factory tests each component before assembling a TV set. This is a unit test.
In fact, we perform unit tests every day. After you write a function, you always need to execute it to check whether the function is normal and sometimes try to output some data, such as the pop-up information window or something, it is also a unit test, which is called a temporary unit test by Lao Na. Software that only performs temporary unit testing is not complete in code testing, and it is difficult to test code coverage by more than 70%. The code that is not covered may leave a large number of small errors, these errors also affect each other. When the BUG is exposed, debugging is difficult, which greatly increases the testing and maintenance costs and reduces the developers' competitiveness. It can be said that full unit testing is the only way to improve software quality and reduce development costs.
For programmers, if they develop the habit of performing unit tests on their own code, they can not only write high-quality code, but also improve the programming level.
To conduct adequate unit tests, the test code should be specially compiled and isolated from the product code. Lao na believes that a relatively simple method is to create a corresponding test project for the product project, create a corresponding test class for each class, and create a test function for each function (except very simple. First of all, let's talk about the concept of Lao Na.
In the era of structured programs, unit testing refers to a function. In today's object-oriented era, unit testing refers to a class. According to the practice of Lao Na, classes are used as testing units, which are complex and operable. Therefore, we still advocate using functions as unit testing units, however, you can use a test class to organize all the test functions of a class. The object-oriented model should not be emphasized in unit tests, because the local code is still structured. Unit Testing requires a lot of work and is simple, practical, and efficient.
One idea is that only the class interfaces (Public Functions) are tested, and other functions are not tested. From the object-oriented perspective, it is true that, however, the purpose of the test is to find an error and finally eliminate the error. Therefore, all functions that are more likely to contain errors must be tested. It has nothing to do with whether the function is private. For C ++, you can use a simple method to separate the functions to be tested: the implementation of simple functions, such as data read/write functions, is written in the header file (inline function ), all functions written and implemented in the source file must be tested (except constructors and destructor ).
When to test? The sooner the unit test is, the better. How early is it? XP Development Theory focuses on TDD, that is, test-driven development. First, write the test code before development. In actual work, it is important to be efficient and comfortable without overemphasizing what comes first. From the old experiences, first write the framework of the product function, then write the test function, write the test case for the function of the product, and then write the code of the product function, each time you write a function, you can run the test to supplement the test case at any time. The framework for writing product functions first refers to the implementation of function null. If there is a return value, a value is returned. After compilation is passed, the test code is written, the function name, parameter table, and return type should all be determined. The tested code written in later may be less likely to be modified.
Who tests it? Unit Testing is different from other tests. unit testing can be considered as part of coding and should be completed by programmers. That is to say, the code that passes the unit test is the completed code, you must also submit the test code when submitting the product code. The test department can perform a certain degree of review.
Regarding the pile code, Lao na believes that the pile code should be avoided in unit testing. The post code is used to replace some code. For example, a product function or test function calls an uncompiled function. You can write a post function to replace the called function, the pile code is also used for test isolation. It is developed in a bottom-up manner. The underlying code is first developed and tested to avoid writing pile code. The advantages of this method are: reduced workload; when testing upper-layer functions, it is also an indirect test of lower-level functions. When the lower-level functions are modified, the regression test can be used to check whether the modification causes errors in upper-level functions.
Test code writing
Most of the articles about unit testing take Java as an example. This article takes C ++ as an example. The unit testing tool described in the second part only introduces the C ++ unit testing tool. The development environment of the following sample code is VC6.0.
Product Type:
Class CMyClass
{
Public:
Int Add (int I, int j );
CMyClass ();
Virtual ~ CMyClass ();
Private:
Int mAge; // age
CString mPhase; // age, such as "Teenagers" and "Teenagers"
};
Create the corresponding test class CMyClassTester. To save the editing width, only the source file code is listed:
Void CMyClassTester: CaseBegin ()
{
// PObj is a member variable of the CMyClassTester class and a pointer to the object of the tested class,
// For simplicity, all test classes can use pObj to name the pointer of the tested object.
PObj = new CMyClass ();
}
Void CMyClassTester: CaseEnd ()
{
Delete pObj;
}
The CaseBegin () and CaseEnd () Functions of the test class create and destroy the tested objects. CaseBegin () must be called at the beginning of each test case, and CaseEnd () must be called at the end ().
Next, we will create a product function example:
Int CMyClass: Add (int I, int j)
{
Return I + j;
}
And the corresponding test function:
Void CMyClassTester: Add_int_int ()
{
}
Use the parameter table as a part of the function name. In this way, when a function is tested with heavy loads, no name conflict occurs. Add a test case as follows:
Void CMyClassTester: Add_int_int ()
{
// The first test case
CaseBegin (); {// 1
Int I = 0; // 2
Int j = 0; // 3
Int ret = pObj-> Add (I, j); // 4
ASSERT (ret = 0); // 5
} CaseEnd (); // 6
}
Rows 1st and 6th create and destroy the tested object. The added {} is to enable the code of each test case to have an independent domain so that multiple test cases use the same variable name.
Rows 2nd and 3rd define input data, and rows 4th call the tested function. These are easy to understand and will not be further explained. Row 5th is the expected output, which is characterized by automatic error reporting when the actual output is different from the expected output. ASSERT is the asserted macro of VC, and other macros with similar functions can be used, when you use a testing tool for unit testing, you can use the asserted macro defined by the tool.
The format in the example is not concise. Rows 2, 3, 4, and 5 can be written into one row: ASSERT (pObj-> Add (0, 0) = 0 ); however, this non-concise format is highly recommended by the boss, because it is clear at a glance, it is easy to establish multiple test cases, and has good adaptability. At the same time, it is also an excellent code document. In short, we recommend that you separate the input data from the expected output.
After creating the first test case, compile and run the test to eliminate syntax errors. Then, use the copy/modify method to create other test cases. Because the differences between test cases are usually very small, you only need to modify one or two pieces of data. Copying/modifying is the quickest way to create multiple test cases.
Test Cases
The following describes the test cases, input data, and expected output. The input data is the core of the test case. It is the external data read by the test function and the initial value of the input data. External data is used for tested functions. In fact, it is data other than local variables. It is divided into several types: parameters, member variables, global variables, and IO media. IO media refers to files, databases, or other media that store or transmit data. For example, if a tested function needs to read data from a file or database, the raw data in a file or database also belongs to the input data. No matter how complicated a function is, it is nothing more than reading, computing, and writing these types of data. Expected output refers to the return value and the result value of the external data written by the tested function. The returned values do not need to be added. The parameters (output parameters), member variables, global variables, and I/O media used for write operations of the tested function all have expected output values. A test case is to set the input data, run the tested function, and then determine whether the actual output meets expectations. Here is an example related to member variables:
Product functions:
Void CMyClass: Grow (int years)
{
MAge + = years;
If (mAge <10)
MPhase = "children ";
Else if (mAge <20)
MPhase = "Teenagers ";
Else if (mAge <45)
MPhase = "Youth ";
Else if (mAge <60)
MPhase = "middle-aged ";
Else
MPhase = "Elderly ";
}
A test case in the test function:
CaseBegin ();{
Int years = 1;
PObj-> mAge = 8;
PObj-> Grow (years );
ASSERT (pObj-> mAge = 9 );
ASSERT (pObj-> mPhase = "children ");
} CaseEnd ();
Assign values to the mAge member variable of the tested class in the input data and assert the value of the member variable in the expected output. Now we can see the benefits of the format recommended by Lao na. This format can be adapted to complicated tests. You can also call other member functions in the input data section. For example, before executing the function to be tested, you may need to read the data in the file and save it to the member variable or connect to the database, lao na calls these operations initialization operations. For example, you can add pObj-> OpenFile (); before ASSERT (...) in the above example ();. To access private members, you can define the test class as a friend class of the product class. For example, define a macro:
# Define UNIT_TEST (cls) friend class cls # Tester;
Then add a line of code in the product class declaration: UNIT_TEST (ClassName ).
The following describes the test case design. As mentioned above, the core of a test case is input data. The expected output is determined based on the input data and program functions. That is to say, if the input data of a program is determined, the expected output can be determined, as for the generation/destruction of tested objects and statements for running tests, all test cases are similar. Therefore, when we discuss test cases, we only discuss input data.
As mentioned above, the input data includes four types: parameters, member variables, global variables, and IO media. In these four types of data, as long as the program to be tested needs to perform read operations, you need to set its initial value. The first two types are more common, and the last two types are less useful. Obviously, it is impossible and meaningless to test all possible values of input data. We should use certain rules to select representative data as input data. There are three main types: normal input, boundary input, and illegal input. Each input can also be classified, that is, the equivalence class method. Each type takes one data as the input data. If the test passes, it is certain that other inputs of the same type can also pass. The following is an example:
Normal Input
For example, the Trim function of a string removes spaces before and after the string. Normal input can be divided into four types: space in front, space in back, space in front, and space in front and back. No space in front and back.
Border Input
In the preceding example, a null string can be considered as a boundary input.
For another parameter indicating age, the valid range is 0-100, and the input boundary has two values: 0 and 100.
Illegal Input
Illegal input is data outside the normal value range, or the Code cannot complete the normal function input. In the preceding example, the age parameter is invalid if it is less than 0 or greater than 100, for another function that performs file operations, illegal input involves the following types: the file does not exist; the directory does not exist; the file is being opened by another program; and the permission is incorrect.
If the function uses external data, normal input will certainly exist, while boundary input and illegal input are not all functions. In general, even if there is no design document, consider the above three inputs to find the basic functions of the function. In fact, unit testing and code writing are two-sided integration. During encoding, the above three inputs must be considered. Otherwise, the robustness of the Code will become a problem.
Four white box coverage
The test data mentioned above is designed for the functions of the program, that is, the so-called black box testing. Unit Testing also needs to design test data from another perspective, that is, to design test cases based on the logic structure of the program, that is, the so-called white-box testing. In the old Na's view, if the black box test is sufficient, then the white box test is unnecessary. Unfortunately, "sufficient" is just an ideal state, for example: are all functional points tested? The functional points of the program are defined by humans and are often incomplete. Some combinations of input data may cause problems. How can we ensure that these combinations have been tested? It is difficult to measure the integrity of the test, which is the main defect of the black box test. The white box test has the advantages of easy to measure the integrity of the test. The two have excellent complementarity. For example: after the function test is completed, the statement coverage rate is calculated. If the statement overwrite is not completed, it is likely that the function points corresponding to the statements not covered are not tested.
The white-box test is a test case designed for the logic structure of the program. The test integrity is measured by the logical coverage rate. Logical units include statement, branch, condition, condition value, condition value combination, and path. Overwrite all statements, and so on. In addition, there is a decision condition coverage, which is actually a combination of branch coverage and condition coverage. We will not discuss it here. There are three types of coverage related to conditions. To explain, conditional coverage covers all conditional expressions, that is, all conditional expressions are calculated at least once, regardless of the calculation result; conditional Value overwrite refers to all possible values of the overwriting condition, that is, the true value and the false value of each condition must be calculated at least once; combination of condition values overwrites all possible combinations of all condition values. Lao Na has done some superficial research and found that the errors directly related to the conditions are mainly logical operator errors, such as: | written as &, and missed the writing! The combination of branch coverage and condition coverage can basically discover these errors. On the other hand, the combination of condition value coverage and condition value coverage usually requires a large number of test cases. Therefore, in the old Na's view, the efficiency-to-cost ratio of the combination of conditional value coverage and conditional value coverage is low. Lao na believes that the test requires a high cost efficiency and sufficient Integrity: to complete the function test, complete the statement overwrite, condition overwrite, branch overwrite, and path overwrite. I am afraid that my friends who have done unit tests will give me a comment on the old requirement: dizzy! Or two-word comment: Crazy! This seems to be an impossible requirement. To achieve this test integrity, the test cost is unimaginable. However, if you don't speak out, the old man puts forward this test requirement, this is because some tools can meet the test requirements at a low cost and will be further introduced later.
For the design of white-box test cases, books in the field of program testing are generally described. The common method is to draw the logical structure diagram of the program, such as the program flowchart or control flow diagram, and design the test case according to the logical structure diagram, these are pure white-box tests, not recommended by the old na. The recommended method is: first complete the black box test, then calculate the white box coverage rate, and design a test case to overwrite it for a non-covered logical unit. For example, first check whether a statement is not overwritten, if a test case is designed to overwrite it, then conditional coverage, branch coverage, and path coverage are completed using the same method. In this way, the integrity of the black box test is verified and repetitive work is avoided, it takes less time to complete a very high test. However, these tasks cannot be completed manually. You must use the tools to introduce the testing tools that can complete these tasks.
Unit 5 Testing Tool
Now we will introduce the unit test tool. There are only three types of tools available in C ++.
The first is CppUnit, which is the originator of the C ++ unit testing tool. It is a free open-source unit testing framework. Since many senior people have written a lot of good articles about CppUnit, It is not ugly for the old man. If you want to know CppUnit, I suggest you read the CppUnit testing framework entry by Cpluser. URL: http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx. This article also provides CppUnit.
Then introduce C ++ Test, which is a product of Parasoft. [C ++ Test is a powerful automated C/C ++ unit-level testing tool that can automatically Test any C/C ++ function or class, automatically generate test cases, test-driven functions, or pile functions. In an automated environment, it is extremely easy to quickly increase the unit-level test coverage to 100%]. The text in [] is taken from http://www.superst.com.cn/softwares_testing_c_cpptest.htm. this is the webpage of huatang Corporation. Lao Na wants to write some text about C ++ Test, but he finds that he cannot go beyond the introduction on the webpage of huatang company. Therefore, it is easy to know about C ++ Test, we recommend that you visit the company's website. C ++ Test, agent of huatang company, can be found if you want to purchase or request a quotation or trial version. I don't know what the benefits will it be if I am an advertisement for Lao nabang huatang company?
Finally, we will introduce Visual Unit (VU), a Chinese Unit testing tool. It is said that it has applied for multiple patents and has a group of innovative technologies. However, lardb only cares about the usefulness and ease of use of Visual Unit. [Automatically generate test code quickly create function test cases program behavior at a Glance high test integrity efficient complete white box coverage fast troubleshooting efficient debugging detailed test report]. The text in [] is excerpted on the webpage of VU developer, URL is: http://www.unitware.cn. Test requirements described earlier: function testing is complete, including statement coverage, condition coverage, branch coverage, and path coverage. It can be easily implemented using VU. It is also worth mentioning: VU can also improve coding efficiency. In general, coding debugging time can be greatly shortened while unit testing is completed. Forget it. I don't want to talk about it any more. I am still interested in introducing theories and experiences, because it can satisfy the vanity of the teacher, but I think it is boring to introduce tools, after all, the tool is not easy to use and is not suitable for use. If you have tried it, you can go to the developer's website and download the demo version and the demo courseware.