Here, the absolute number of possible concrete input values for the input data (a single parameter of the test object) is divided into partitions (also called “classes”). An equivalence partition contains all data values that the tester expects the test object to process in the same way. In other words, it is assumed that testing only one member of an equivalence partition is sufficient.
The input value range is divided into equivalence partitions
Alongside partitions for valid input, you also need to create partitions that contain invalid input values.
Case Study: Equivalence partitioning
We recall: the software enables the manufacturer to give its dealers various discounts. The corresponding requirement reads thus:
For prices below $15,000 there is no discount. For prices up to $20,000, a discount of 5% is appropriate. If the price is below $25,000, a 7% discount is possible. If the price is above $25,000, a discount of 8.5% is to be applied.
Four equivalence partitions containing valid input values (vEP) can easily be derived for calculating the discount:
Table 5-1Valid equivalence partitions and representatives
We used the representatives 14500, 16500, 24750, and 31800 (see table 2-3). Each of these values represents one of the four equivalence partitions. It is assumed that a test run using the values 13400, 17000, 22300, and 28900 would reveal no further failures and therefore doesn’t need to be performed. Tests using boundary values (such as 15000) are discussed.
Equivalence partitions with invalid values
You need to create equivalence partitions with invalid input values and test these too.
Case Study
For our example, we use the following invalid equivalence partitions2 (iEP):
Table 5-2Invalid equivalence partitions and representatives
To systematically derive test cases, you first have to define a range for each input variable (for example, function or method parameters for a component test, or form fields for a system test). This range constitutes the equivalence partition for all valid input values, and the test object has to process them all according to the specifications. Values outside of the range belong to the equivalence partition of invalid input values, and you also need to check the test object’s behavior using these.
Sub-dividing equivalence partitions
The next step involves fine-tuning the equivalence partitions you have established. Any elements of an equivalence partition that, according to the specifications, are processed differently are assigned to a new equivalence sub-partition. You then need to keep splitting the equivalence partitions until each requirement has its own corresponding partition, from which a representative is selected for testing.
As well as a representative, each test case includes the expected result and, if necessary, any preconditions required for the test to run.
Equivalence partitions for output and other values
This principle of breaking things down can be applied just as well to output values. However, deriving test cases is more difficult because you also need to determine the corresponding input value that results in each representative output value. Additional equivalence partitions for invalid output values need to be established too.
You can build equivalence partitions for any coherent data element used by the test object (i.e., not just input and output values). These can be internal values, time-related values (for instance, before and after an event), or interface parameter values that are checked during integration testing.
Make sure that every input/output value belongs to only one equivalence partition. You need to take care when dividing values into equivalence partitions and selecting representatives, as this strongly influences which test cases are performed and therefore the likelihood of successfully identifying failures. Deriving equivalence partitions from specifications and other documents is definitely a non-trivial task.
Equivalence partition boundaries
High-quality test cases test the boundaries of an equivalence partition, where ambiguities and inaccuracies in the requirements are often found. It is difficult to formulate precise mathematical situations using written language, and this makes it tricky to decide which boundary values belong to which equivalence partition. The written requirement “… less than $15,000 …” can be represented by the precise value 15000 within the equivalence partition (EP: x ≤ 15000) or outside it (EP: x < 15000). An additional test case using x = 15000 could reveal a possible misinterpretation and a hitherto undiscovered failure goes into detail on boundary value analysis.
Case Study: Equivalence partitions with integer input values
The following example illustrates the principle of forming an equivalence partition using integer input values. The integer parameter extras for the calculate_price() method gives us the following equivalence partitions:
Table 5-3Equivalence class with integer input values
Note that, unlike in the world of mathematics, the value range in a computer is limited by given maxima and minima. Using values outside of this range usually causes failures, as they cannot be processed properly by the system.
The equivalence partition of invalid values is made up of all values that are non-numerical5 or larger or smaller than the range boundary values. If we assume that the method always reacts the same way to invalid input values (for example, an exception handler that returns the error code NOT_VALID), it is sufficient to assign all invalid input values to a single equivalence partition called NaN (“Not a Number”). Floating-point numbers are included in this partition, as it is assumed that an input value such as 3.5 will also return an error message. In this case, we don’t need to form any equivalence sub-partitions, as all incorrect input produces the same result. However, an experienced tester will include a test case for a floating-point number to check that the system doesn’t round non-integer input values up or down and use the result for subsequent calculations. This is a great example of an experience-based test case.
In most cases positive and negative values need to be handled differently, so it makes sense to further divide the equivalence partition for valid values (vEC1). Zero is an input value that often causes failures.
Table 5-4Equivalence partitions and representatives for integer input values
For each equivalence partition we chose a random representative and added the boundary values MIN_INT, -1, 0, and MAX_INT (see section 5.1.2). Strictly speaking, there are no boundary values for the equivalence partition for invalid values.
In the end, equivalence partitioning (including boundary values) gives us a total of seven values for the integer parameter extras:
{“f“, MIN_INT, -123, -1, 0, 654, MAX_INT}.
We now have to define the expected result or test object reaction for each, so that we can effectively decide whether the test reveals a failure or not.
Equivalence partitions for atypical input data types
The integer input values we used in our example make it simple to build equivalence partitions and select representatives. As well as elemental data types, the value range can be made up of composite data structures or object sets. Here too, we have to select appropriate representatives.
Case Study: Selecting input values from a set
The following example helps to illustrate the principle. If the test object reacts differently to different types of customer (e.g., employee, student, trainee, senior), each different type needs to be covered by its own individual test case. If the test object’s expected reaction is the same regardless of the customer type, one test case based on one of the types should be sufficient.
The EasyFinance module is sure to deliver different results for each customer type (employee, student, trainee, senior) and thus requires four separate test cases. Precise details of what to test can be derived from the requirements. Each resulting calculation needs to be tested for potential failures.
For the DreamCar online configuration module it is probably sufficient to test just one of the four customer types. It is likely that the software doesn’t care who is configuring a vehicle, so the representative here can be a student or a senior (although once again, we need to look to the requirements for precise details). However, the tester needs to be aware that if the test is performed using the “employee” input value, this provides no evidence that the chosen configuration works properly for the other customer types.
Our Tip Building equivalence partitions
Here are some guidelines for building equivalence partitions:
- Establish the specified limits and conditions for input and output values.
- Build equivalence partitions for each limit and/or condition:
- If a contiguous value range is specified, you need to build one “valid” and two “invalid” equivalence partitions.
- If the specifications state that a specific number of characters has to be entered (for example, a name consisting of at least five and a maximum of seven letters), you need to build a “valid” equivalence partition that includes all possible valid values, and two “invalid” equivalence partitions that cover “less than 5” and “more than 7”.
- If a set of values is specified that requires different handling, you need to build an equivalence partition for each individual value in the set, and one “invalid” equivalence partition.
- If a limit or condition describes a situation whose fulfillment is obligatory, you need to build two equivalence partitions—one for “valid” and one for “invalid”.
- If you have any doubts about whether values within an equivalence partition are to be handled the same way, you need to further divide the partition to avoid ambiguities.
Test Cases
Rules for designing test cases
A test object will usually have more than one input parameter. Equivalence partitioning provides at least two equivalence partitions for each parameter (for valid and invalid values), and thus at least two representative test input values.
When specifying a test case, you have to assign an input value to each parameter, so you have to decide which of the available representatives can be combined to form an input data set. In order to provoke all necessary test object reactions (according to the decomposition of the equivalence partitions), you need to observe the following rules when combining representatives:
Combining representatives
- For the “valid” equivalence partitions, your test cases have to cover all possible combinations of representatives. Each of these combinations forms a “valid test case”.
Test invalid values separately
- A representative from an “invalid” equivalence partition should only be combined with representatives from other, valid equivalence partitions. In other words, every “invalid” equivalence partition requires an additional “negative” test case (see below).
Limiting the set of test cases
The total number of “valid” test cases is therefore the product of the number of valid equivalence partitions per parameter. This type of multiplicative combination can quickly produce hundreds of valid test cases for just a few parameters. It is rarely possible to deal with so many test cases, so we need to apply rules that help to reduce their number:
- Combine representatives from all test cases and sort them according to the frequency of typical usage profiles, then prioritize them according to this sequence. This ensures that you only use the test cases that are most relevant to real-world usage scenarios.
- Preference is given to test cases that cover boundary values or combinations of boundary values.
- Make sure that every representative in an equivalence partition is combined with every representative in the other equivalence partitions used in a test case (i.e., pair-wise combination instead of exhaustive combination).
- Your minimum requirement should be that every representative occurs in at least one test case.
- Don’t combine representatives from equivalence partitions with invalid values.
Test invalid values separately
Representatives from invalid equivalence partitions are not combined multiplicatively. An invalid value should only be combined with valid ones. This is because an invalid parameter value triggers an exception regardless of the other parameter values. Combining multiple invalid values in a single test case quickly leads to mutual fault masking, with only one of the possible exceptions being triggered. Furthermore, when an exception does occur, it is not clear which of the invalid values caused it and unnecessary further analysis ensues7.
Case Study: Testing DreamCar price calculations
Once again, the following example uses the VSR-II DreamCar module’s calculate_price() method as its test object. The test serves to check whether the method calculates a correct total price for all input values according to the system’s specifications. The functional specifications and its interface are known, but its inner workings remain a black box:
double calculate_price (
double baseprice, // Vehicle base price
double specialprice, // Special edition markup double
double extraprice, // Price of optional extras
int extras, // Number of optional extras
double discount // Dealer discount
Step 1 Identify the range
We use equivalence partitions to design the input parameter test cases. The first step is to establish the range for each input parameter, which provides us with equivalence partitions containing valid and invalid values (see table 5-5).
Table 5-5Valid and invalid equivalence partitions for our method’s parameters
The specifications alone provide us with a valid and an invalid equivalence partition for each parameter.
Step 2 Fine-tuning equivalence partitions based on the specifications
In order to divide these equivalence partitions, we require further information about the functionality of the method, which we glean from the method’s specifications. These deliver the following statements that are relevant to our test:
- Parameters 1-3 are (vehicle) prices. Prices are never negative. No price limits are specified.
- The discount given for optional extras (10% for extras ≥ 3, 15% for extras ≥ 5) depends on the extras value. extras represents the selected number of optional extras and is thus not negative8. No upper limit to the number of extras is specified.
- The discount parameter is given as a percentage between 0 and 100. Because the discount brackets for extras are specified as percentages, the tester can assume that the dealer discount is given as a percentage too. However, it would be prudent to check this with the customer.
Gaps in the requirements
These considerations are based on the functional specifications, which reveal a number of gaps. The tester “fills” these gaps by making plausible assumptions based on common sense and testing experience, or on information gained by talking to other testers and developers. In case of doubt, it is always best to talk directly to the customer about what is really required. This analysis enables us to further decompose the equivalence partitions we have built. The more precise the partitioning, the more accurate the resulting test will be. The partition process is complete when all the specified conditions and all the tester’s experience-based assumptions are covered.
Table 5-6Partitioning the equivalence partitions for the calculate_price() method’s parameters
This results in a total of 7 equivalence partitions for valid parameters and 11 for invalid parameters.
Step 3 Selecting representatives
To derive test input values, we have to select a representative for each equivalence partition. Equivalence partition theory tells us that this can be any value within the range covered by the equivalence partition. However, in practice, the decomposition of an equivalence partition seldom works perfectly. Partitioning usually stops at a particular level, due either to a lack of detailed information, a lack of time, or simply because it is too much effort. You may find that some equivalence partitions overlap9. When selecting representatives, note that a single equivalence partition can contain values that cause varying reactions in the test object, so values that occur more often when the system is in everyday use.
The valid representatives selected in the case study example (see table 5-6 above) are plausible values that we assume will occur regularly when the system is in use.
Step 4 Combining test cases
The next step involves combining representatives to build test cases. The rules listed above give us 1×1×1×3×1 = 3 “valid” test cases (through multiplicative combination) and 2+2+2+2+3 = 11 “invalid” test cases (through separate tests for each invalid representative). This gives us a total of 14 test cases for 18 equivalence partitions (see table 5-7).
Table 5-7Test cases for the calculate_price() method
For the valid partitions that are combined with an invalid partition, we used the same representatives for our test cases to ensure that changing only the invalid parameter provokes the expected reaction in the test object.
Because four of the five parameters have only one valid equivalence partition, there are only a few “valid” test cases. In other words, there is no reason to further reduce the number of test cases.
Once the test cases have been set up, the expected result has to be determined. In the case of the “invalid” test cases in the table above, all we have to do is enter the error message generated by the test object. The expected results for the “valid” tests have to be calculated separately, perhaps using a spreadsheet.
Our Tip
- Table 5-7 lists all the test cases for the calculate_price() method, including ones like #10 that tests a negative number of optional extras. In practice, a negative value like this won’t be handed over to the calculate_price() method, but instead checked and intercepted in advance (see Design by Contract [Meyer 13]).
Leave a Reply