Http://yangyubo.com/unit-testing-guidelines/.
Source: http://geosoft.no/development/unittesting.html
Translator (yospaly:
If no detailed specification proved by practice is provided during the implementation of unit tests, it is difficult to grasp the "degree" of the tests and the scope is too small to display, too large and infringing on "others'" territory. god belongs to God, and Caesar belongs to Caesar. It is not a bad thing to give full play to the power of unit testing and provide motivation for code restructuring and improving code quality.
This document comes from geotechnical and is a rare experience criterion. You can use this criterion as a template and combine the experience of your team to develop an internal unit test criterion.
Test principles:
1. Keep unit testing small and fast
2. unit testing should be fully automated/non-interactive
3. Make unit tests easy to run
4. Evaluate the test
5. Fix failed tests immediately
6. Maintain the test at the unit level.
7. Simplified to traditional
8. Maintain test independence
9. Keep tests close to the class being tested
10. Rational naming of Test Cases
11. test only public interfaces
12. Black Box
13. As a white box
14. The sesame function should also be tested.
15. Pay attention to the execution coverage rate first
16. overwrite the Boundary Value
17. Provide a random value Generator
18. Each feature is tested only once.
19. Use explicit assertions
20. provide reverse Testing
21. Keep in mind testing during code design
22. Do not access predefined external resources
23. Weigh test costs
24. Reasonably arrange test priorities
25. Prepare for test failure
26. Write test cases to reproduce bugs
27. Understanding limitations
1. Keep unit testing small and fast
Theoretically, all test suites should be fully run before any code check-in, so keeping the test code light can reduce the development iteration cycle.
2. unit testing should be fully automated/non-interactive
Test suites are usually executed on a regular basis and the execution process must be fully automated to make sense. Tests that require manual check for output results are not a good unit test.
3. Make unit tests easy to run
Configure the development environment. It is best to run a single test case and test suite by typing a command or clicking a button.
4. Evaluate the test
Perform coverage analysis on the executed tests to obtain precise code execution coverage and investigate which codes are not executed.
5. Fix failed tests immediately
Every developer should ensure that the test cases of the new check-in can run successfully, and the existing test cases of the code check-in can also pass.
6. Maintain the test at the unit level.
Unit testing is a test of classes. A "test class" should correspond to only one "tested class", and the test environment for "tested class" behavior should be isolated. you must be careful not to use the unit test framework to test the workflow of the entire program. Such a test is inefficient and difficult to maintain. workflow testing (cross-module/class data flow testing) has its own territory, but it is never a unit test and must be set and executed separately.
7. Simplified to traditional
A simple test is far better than a complete test. A simple "test class" will encourage the establishment of a basic testing skeleton of the "tested class", and check the effectiveness of the building environment, unit testing environment, execution environment, and coverage analysis tools, at the same time, ensure that the "tested classes" can be integrated and called.
The following is the Hello, world! unit test version! :
Void testdefaultconstruction ()
{
Foo = new Foo ();
Assertnotnull (FOO );
}
8. Maintain test independence
To ensure the stability, reliability, and ease of maintenance of tests, test cases must not depend on each other or on the execution sequence.
9. Keep tests close to the class being tested
I have not translated this rule. I personally think this rule is debatable. Most C ++ and Python libraries separate the test code from the functional code directory, it is usually to create a tests directory at the same level as the src directory, and the test prefix is often not added before the tested module/class name. this ensures that the functional code and test code are isolated, the directory structure is clear, and the test cases are easier to exclude when the source code is released.]
If the class to test is foo the test class shocould be called footest (not testfoo) and kept in the same package (directory) as Foo. keeping test classes in separate directory trees makes them harder to access and maintain.
Make sure the Build Environment is configured so that the test classes doesn' t make its way into production libraries or executables.
10. Rational naming of Test Cases
Make sure that each test method only tests a specific feature of the "tested class" and name the test method accordingly. the typical naming conventions are test [what], such as testsaveas (), testaddlistener (), and testdeleteproperty.
11. test only public interfaces
Unit tests can be defined as testing classes through public APIs of classes. some test tools allow testing of private members of a class, but this practice should be avoided, which makes the test cumbersome and more difficult to maintain. if a private member does need to perform a direct test, you can consider refactoring it to the public method of the tool class. but it should be noted that this is done to improve the design, rather than to help the test.
12. Black Box
From the perspective of third-party users, test whether the class meets the specified requirements. and try to let it go wrong (in the original article tear it apart, the intention is to "Tear it up", my understanding is a crash, a problem, cannot work correctly ).
13. As a white box
After all, the tested classes are self-written and self-tested by programmers. You should spend more time testing in the most complex logic.
14. The sesame function should also be tested.
It is recommended that all important functions be tested. Some sesame methods, such as simple setter and getter, can be ignored. However, there are still good reasons to support testing sesame functions:
• Zhima is hard to define. Different people have different understandings.
• From the Perspective of black box testing, you cannot know which codes are common.
• Even sesame functions may contain errors, which are often the consequence of "copy and paste" code:
Private double weight _;
Private Double X _, Y _;
Public void setweight (INT weight)
{
Weight = weight _; // Error
}
Public double getx ()
{
Return X _;
}
Public double Gety ()
{
Return X _; // Error
}
Therefore, it is recommended to test all the methods. After all, the sesame function is also easy to test.
15. Pay attention to the execution coverage rate first
The execution coverage rate and actual test coverage rate are differentiated. the initial goal of the test should be to ensure high execution coverage. this ensures that the code can be effectively executed when some parameters are entered. once the coverage rate is ready, you should begin to improve the test coverage rate. note that the actual test coverage rate is hard to measure (and tends to approach 0% ).
Consider the following public methods:
Void setlength (double length );
Call setlength (1.0) and you may get a 100% execution coverage rate. to reach the actual test coverage rate of 100%, the method of how many double floating point numbers must be called and the correctness of the behavior should be verified one by one. this is undoubtedly an impossible task.
16. overwrite the Boundary Value
Make sure that the parameter boundary value is overwritten. for numbers, test negative numbers, 0, positive numbers, minimum values, maximum values, Nan (non-numeric), and infinity. for a string, test the Null String, single character, non-ASCII string, multi-byte string, and so on. for the set type, test null, 1, first, last, and so on. for the date, the test may January 1, may February 29, May December 31, and so on. the tested class itself implies some boundary values under certain conditions. the basic point is to test these boundary values as thoroughly as possible, because they are all major "suspects ".
17. Provide a random value Generator
When the boundary value is overwritten, another simple way to further improve the test coverage is to generate random parameters so that each test is executed with different inputs.
To do this, you need to provide a tool class for generating random values of basic types (such as floating point, integer, String, date, etc.). The generator should cover all the value ranges of various types.
If the test time is short, you can wrap it in a loop to overwrite as many input combinations as possible. the following example shows whether the original value is returned after two conversions of little endian and big endian in byte order. because the test process is fast, it can be run for 1 million times.
Void testbyteswapper ()
{
For (INT I = 0; I <1000000; I ++ ){
Double V0 = random. getdouble ();
Double V1 = byteswapper. Swap (v0 );
Double v2 = byteswapper. Swap (V1 );
Assertequals (v0, V2 );
}
}
18. Each feature is tested only once.
In the test mode, assertion can be abused. This method may lead to more difficult maintenance and needs to be avoided. Only explicit testing of the features indicated by the test method name is required.
For general code, ensuring that the test code is as few as possible is an important goal.
19. Use explicit assertions
Assertequals (a, B) instead of asserttrue (A = B) should always be used first, because the former will give more meaningful information about why the test fails. this rule is especially important when you are not sure about the input value beforehand. For example, we used a combination of random parameter values.
20. provide reverse Testing
Reverse testing refers to the deliberate compilation of problem code to verify robustness and correct error handling.
If the parameters in the following method are negative, an exception is immediately thrown:
Void setlength (double length) throws illegalargumentexcepti
You can use the following method to test whether the exception is correctly handled:
Try {
Setlength (-1.0 );
Fail (); // if we get here, something went wrong
}
Catch (illegalargumentexception exception ){
// If we get here, all is fine
}
21. Keep in mind testing during code design
The cost of writing and maintaining unit tests is very high. Reducing public interfaces and loop complexity in code is an effective way to reduce costs and make high-coverage testing code easier to write and maintain.
Some suggestions:
• Make the class member constant and initialize it in the constructor. Reduce the number of setter methods.
• Restrict excessive use of inheritance and public virtual functions.
• Reduce public interfaces by using a friend Meta class (C ++) or package scope (Java.
• Avoid unnecessary logical branches.
• Write as few code as possible in the logical branch.
• Use exceptions and assertions to verify the validity of parameter parameters in public and private interfaces.
• Restrict the use of shortcuts. For black boxes, all methods must be tested equally. Consider the following short example:
Public void Scale (double x0, double y0, double scalefactor)
{
// Scaling Logic
}
Public void Scale (double x0, double y0)
{
Scale (x0, y0, 1.0 );
}
Deleting the latter can simplify the test, but the workload of user code will also increase slightly.
22. Do not access predefined external resources
The unit test code should not assume an external execution environment so that it can be executed at any time/anywhere. In order to provide necessary resources to the test, these resources should be provided by the test itself.
For example, a class that parses a certain type of file can embed the file content into the test code, write it to the temporary file during the test, and delete the file after the test ends, instead of directly reading from the specified address.
23. Weigh test costs
The cost of writing a unit test is high, but the cost of writing a unit test is also high. We need to make a proper balance between the two. If we use the execution coverage rate to measure, the industry standard is usually around 80%.
Typically, error handling and Exception Handling for read and write external resources cannot reach execution coverage. A fault occurs when the simulated database processes half of the transaction. However, it may be too costly for a large range of code reviews.
24. Reasonably arrange test priorities
Unit testing is a typical bottom-up process. If you do not have enough resources to test all modules of a system, you should first focus on the lower-layer modules.
25. Prepare for test failure
Consider the following example:
Handle handle = manager. gethandle ();
Assertnotnull (handle );
String handlename = handle. getname ();
Assertequals (handlename, "handle-01 ");
If the first assertion fails, the subsequent statement will cause the code to crash and the rest of the tests will not be executed. prepare for test failure at any time to prevent the execution of the entire test suite from being interrupted for a single failed test item. the preceding example can be rewritten:
Handle handle = manager. gethandle ();
Assertnotnull (handle );
If (handle = NULL) return;
String handlename = handle. getname ();
Assertequals (handlename, "handle-01 ");
26. Write test cases to reproduce bugs
Every time a bug is reported, you must write a test case to reproduce the bug (that is, you cannot pass the test) and use it as the standard for successful code correction.
27. Understanding limitations
Unit Tests will never prove code correctness
A failed test may indicate a code error, but a successful test cannot prove anything.
The most effective application scenarios of unit testing are verification and regression testing: when new features are added and code is restructured, will the correctness of old functions be affected?