TESTING
SOFTWARE QA
RESOURCES
Notes taken from Glenford Myer's The Art of Software Testing.

Apart from psychological, the most important consideration in program testing is the design or invention of effective test cases. The reason for the importance of test-case design stems from the fact that "complete" testing is impossible and therefore a test of any program must be necessarily incomplete (i.e., the testing cannot guarantee the absence of all errors). The obvious strategy, then, is to try to reduce this incompleteness as much as possible.

Given constraints on time, cost, computer time, etc., the key issue of testing becomes

What subset of all possible test cases has the highest probability of detecting the most errors?
The study of test-case-design methodologies supplies one with answers to this question.

Probably the poorest methodology of all is random-input testing—the process of testing a program by selecting, at random, some subset of all possible input values. In terms of the probability of detecting the most errors, a randomly selected collection of test cases has little chance of being an optimal, or close to optimal, subset. What we look for is a set of thought processes that allow one to select a set of test data more intelligently. Exhaustive black-box and white-box testing are, in general, impossible, but a reasonable testing strategy might use elements of both. One can develop a reasonably rigorous test by using certain black-box-oriented test-case-design methodologies and then supplementing these test cases by examining the logic of the program (i.e., using white-box methods).

Example methodologies are:

Black box White box
Equivalence partitioning Statement coverage
Boundary-value analysis Decision-coverage
Cause-effect graphing Condition-coverage
Error guessing Decision/condition-coverage
Multiple-condition-coverage

It is recommended that most, if not all, of the methods be used to design a rigorous test of a program, since each method has distinct strengths and weaknesses (e.g., types of errors it is likely to detect and overlook). After studying the methods, one might have reservations about this statement, since all the methods are mentally taxing, requiring a lot of hard work. One must realize, however, that program testing is inherently an extremely difficult task. To quote an old sage, "If you thought designing and coding that program was hard, you ain't seen nothing yet."

The recommended procedure is to develop test cases using the black-box methods and then develop supplementary test cases as necessary by using the white-box methods.

White-box testing is concerned with the degree to which test cases exercise or cover the logic (source code) of the program. The ultimate white-box test is the execution of every path in the program, but since in a program with loops the execution of every path is usually infeasible, complete path testing is not considered here as a viable testing goal.

If one backs completely away from path testing, it may seem that a useful coverage criterion is to require every statement in the program to be executed at least once. Unfortunately, this is a weak criterion, so we can say that writing enough test cases such that every statement is executed at least once is a necessary, but in no way sufficient, criterion for a reasonable white-box test.

A well chosen set of test design strategies forms a test plan.

Test-case-design methodologies can be combined into an overall strategy. The reason for combining them should be obvious: each contributes a particular set of useful test cases, but none of them by itself contributes a thorough set of test cases. A reasonable strategy is:

1. If the specification contains combinations of input conditions, start with cause-effect graphing.

2. In any event, use boundary-value analysis. Remember that this is an analysis of input and output boundaries. The boundary-value analysis yields a set of supplemental test conditions, but many or all of these can be incorporated into the cause-effect tests.

3. Identify the valid and invalid equivalence classes for the input and output, and supplement the test cases identified above if necessary.

4. Use error-guessing to add additional test cases.

5. Examine the program's logic with regard to the set of test cases. Use either the decision-coverage, condition-coverage, decision/condition-coverage, or multiple-condition-coverage criterion (the last being the most complete). If the coverage criterion has not been met by the test cases identified in the prior four steps, and if meeting the criterion is not impossible (i.e., certain combinations of conditions may be impossible to create because of the nature of the program), add sufficient test cases to cause the criterion to be satisfied.