"Triple Threat" Test Cases?In software testing, the terms "precondition" and "postcondition" are used, by some, almost synonymously with "test case." It should be no surprise, then, that when it comes to testing, the preconditions, postconditions, and invariants of the model-based specification, taken as a unit, are a veritable triple threat test case. Let's look at why this is so. Threat #1The PreconditionWhen it comes to testing, a key question is always what test points to select. A test point is a specific value selected for an input or state variable to make a test case. Preconditions are an ideal source for test point selection both in terms of valid test cases and failure scenario testing (i.e., tests of the software's error-handling capabilities). Why is this? Remember that a precondition is that set of values for an input or state variable that satisfies both the postcondition and the invariant so that it forms a type of "boundary" between valid test points (those that satisfy the postcondition and invariant) and failure scenario test points (those that satisfy one or the other but not both). Hence, preconditions often provide just the detail needed to apply testing techniques that involve boundary value analysis where you pick test points on, off, or inside "boundaries." Threat #2The PostconditionThe postcondition is that part of a model-based specification that describes what a use case step does, in terms of how it modifies the model. In that sense, it is the very heart of model-based specification. For testing, the postcondition is the primary means of stating what the expected result of a test case should be. As Boris Beizer (1990) has said, test cases without expected results are like a game of billiards where you don't call the pocket until after the ball goes in. Threat # 3The InvariantAnd finally, rounding out the triple threat is the invariant. One might think that the role of the invariant is pretty much done after the precondition has been calculated. But invariants play several supporting roles in test cases. First, the invariant can be used in a test case to cross-check the results of the postcondition. Recall that the postcondition and invariant act like simultaneous equations: any actual result produced by the software must pass the check of not only the postcondition (primary expected result), but also a cross-check against the invariant. This provides an extra boost to the expected result of the test case. This is particularly true in situations where you have not derived a precondition. The invariant can essentially act as a fallback precondition "after the fact"; i.e., a precondition is a check to see if conditions are good before execution of an operation. An invariant can be used to perform the same check after the operation executes; as the saying goes, "Better late than never!" Second, you will recall that global invariants are in a sense, preconditions on steroids.[1] Whereas a regular precondition guards one specific postcondition, global invariants act as guards to all postconditions that use the state variables covered in the global invariant. From a failure analysis and testing perspective, global invariants are system properties that can be tested for repeatedly. For example, in our widget shipping example from the first chapter, the global invariant WidgetsInStock áE0 is a mini test case that can be performed repeatedly throughout all scenarios of the use case, and even across all use cases (given that the scope of "global" is determined to be application wide, e.g., it's a business rule of the underlying database). If at any time it is found that WidgetsInStock < 0, something has gone wrong and the potential for failure is there.
In summary, preconditions, postconditions, and invariants are ideal for test design for the following reasons:
The next section looks at applying this testing trio to the design of test cases for the chemical tank example from Chapter 5. |