Recommended approach to developer testing
1. Test for each relevant requirement to make sure that the requirements have been implemented. Plan the test cases for this step at the requirements stage or as early as possible, preferably before you begin writing the unit to be tested.
2. Test for each relevant design concern to make sure that the design has been implemented. Plan the test cases for this step at the design stage or as early as possible, before you begin the detailed coding of the routine or class to be tested.
3. Use "basis testing" to add detailed test cases to those that test the requirements and the design.
Test first or test last?
1. Writin test cases before writing the code doesn't take any more effort than writing test cases after the code; it simply resequences the test-case-writing activity.
2. When you write test cases first, you detect defects earlier and you can correct them more easily.
3. Writing test cases first forces you to think at least a little bit about the requirements and design before writing code, which tends to produce better code.
4. Writing test cases first exposes requirements problems sooner, before the code is written, because it's hard to write a test case for a poor requirement.
5. If you save your test cases(which you should), you can still test last, in addition to testing first.
Limitations of developer testing.
1. Developer tests tend to be "clean tests". Developer tend to test for whether the code works(clean tests) rather than to find all the ways the code breaks(dirty tests).
2. Developer tests tend to have an optimistic view of test coverage.
3. Developer tests tend to skip more sophisticated kind of test coverage.
Bag of Testing Tricks
Structured basis testing
Is a fairly simple concept. The idea is that you need to test each statement in a proram at least once. The easiest way to make sure that you've gotten all the bases covered is to calculate the number of paths through the program and then develop the minimum number of test cases that will exercise every path through the program.
1. Start with 1 for the straight path through the routine.
2. Add 1 for each of the following keywords, or their equivalents: if, while, repeat, for, and, and or.
3. Add 1 for each case in a case statement. If the case statement doesn't have a default case, add 1 more.
Data-flow testing is based on the idea that data usage is at least as error-prone as control flow.
Data can exist in one of three states:
1. Defined. The data has been initialized, but it hasn't been used yet.
2. Used. The data has been used for computation, as an argument to a routine, or for someting else.
3. Killed. The data was once defined, but it has been undefined in some way.
In addition to the three satement, it's convenient to have terms that describe entering or exiting a routine immediately before or after doing something to a variable:
1. Entered. The control flow enters the routine immediately before the variable is acted upon. A working variable is initialized at the top of a routine, for example.
2. Exited. The control flow leaves the routine immediately after the variable is acted upon. A return value is assigned to a status variable at the end of a routine, for example.
Combinations of data states
The normal combination of data states is that a variable is defined, used one or more times and perhaps killed. but some is suspicious:
1. Defined-Defined. If you have to define a variable twice before the value sticks,it's wasteful and error-prone.
2. Defined-Exited. If the variable is a local variable, it doesn't make sense to define it and exit without using it. If it's a routine parameter or a global variable, it might be all right.
3. Defined-Killed. It suggests either that the variable is extraneous or that the code that was supposed to use the variable is missing.
4. Entered-Killed. This is a problem if the variable is a local variable. It wouldn't need to be killed if it hasn't been defined or used. but if it's a routine parameter or a global variable, it's all right as long as the variable is defined somewhere else before it's killed.
5. Entered-Used. It's a problem if the variable is a local variable.
6. Killed-Killed. A variable shouldn't need to be killed twice.
7. Killed-Used. Using a variable after it has been killed is a logical error.
8. Used-Defined. Using and then defining a variable might or might not be a problem, depending on whether the variable was also defined before it was used.
In addition to the formal test techniques, good programmers use a variety of less formal, heauristic techniques to expose errors in their code.
1. Boundary analysis--off by one errors.
2. classes of bad data
Too little data
Too much data
The wrong kind of data
The wrong size of data
Keeping Test Records
1. Administrative description of th defect.
2. Full description of the problem.
3. Steps to take to repeat the problem
4. suggested workaround for the problem
5. Related defects.
6. severity of the problem.
7. Origin of the defect-requirement, design, coding or testing.
8. Subcalssification of a coding defect--off by one, bad assignment, bad array index, bad routine call, and so on.
9. Location of the fix for the defect.
10. Classes and routines changed by the fix.
11. Person responsible for the defect(this can be controverial and might be bad for morale).
12. Lines of code affected by the defect.
13. Hours to find the defect.
14. Hours to fix the defect.