Lesson 4 of 11 · Software Testing Fundamentals
Test Case Design Techniques
Equivalence partitioning, boundary value analysis, decision tables, state transitions, pairwise and experience-based techniques, with worked examples you can reuse.
beginner 9 min read Updated
You will learn to
- Derive test cases with equivalence partitioning and boundary value analysis
- Model business rules as decision tables and workflows as state transition diagrams
- Reduce combinations with pairwise testing
- Combine black-box techniques with error guessing and exploratory testing
Prerequisites
- Introduction to Software Testing
- STLC: The Software Testing Life Cycle
Because exhaustive testing is impossible, every tester faces the same question: out of the effectively infinite inputs and paths, which handful of tests should I actually run? Test design techniques are systematic answers to that question. They help you pick tests that are most likely to reveal defects, explain why you chose them, and show what you deliberately left out.
These techniques appear in the ISTQB Foundation syllabus and in almost every QA interview. More importantly, they are the difference between “I tried a few things” and a test set you can defend.
Three families of techniques
| Family | Based on | Examples |
|---|---|---|
| Black-box (specification-based) | Requirements and behaviour, without looking at code | Equivalence partitioning, boundary value analysis, decision tables, state transition, pairwise |
| White-box (structure-based) | The code’s internal structure | Statement coverage, branch coverage |
| Experience-based | The tester’s knowledge and intuition | Error guessing, exploratory testing, checklists |
This lesson focuses on black-box and experience-based techniques, which you can apply from day one without access to the code.
Equivalence partitioning
Idea: divide inputs into groups (partitions) that the system should treat the same way. If one value in a partition works, the others should too, so you test one value per partition.
Example: an age field for an insurance quote accepts ages 18 to 65 inclusive. Ages must be whole numbers.
| Partition | Range | Valid? | Representative value |
|---|---|---|---|
| Below minimum | 17 and below | Invalid | 10 |
| Accepted | 18 to 65 | Valid | 40 |
| Above maximum | 66 and above | Invalid | 80 |
| Not a whole number | e.g. 30.5 | Invalid | 30.5 |
| Not a number | e.g. “abc” | Invalid | abc |
| Empty | blank | Invalid | (empty) |
Six tests instead of hundreds, with each test standing in for a whole class of inputs.
Rules of thumb:
- Partition outputs too, not only inputs. If quotes fall into price bands, each band is a partition.
- Test invalid partitions one at a time. If you send an invalid age and an invalid email together, you cannot tell which one the error message is about; one may mask the other.
Boundary value analysis
Idea: defects cluster at the edges of partitions, because that is where developers write comparisons like > versus >=. Boundary value analysis (BVA) tests values on and around each boundary.
There are two common variants:
- Two-value BVA: for each boundary, test the boundary value and its nearest neighbour in the adjacent partition.
- Three-value BVA: test the boundary value and the values just below and just above it.
For the 18 to 65 age rule:
| Boundary | Two-value BVA | Three-value BVA |
|---|---|---|
| Lower (18) | 17, 18 | 17, 18, 19 |
| Upper (65) | 65, 66 | 64, 65, 66 |
Why this catches bugs: if the developer wrote age > 18 instead of age >= 18, only the test with exactly 18 reveals it. A “typical” value like 40 never will.
// The defect that BVA is designed to catch
function isEligible(age) {
return age > 18 && age <= 65; // bug: 18 should be accepted
}
isEligible(40); // true - passes, hides the bug
isEligible(18); // false - boundary test exposes it
Boundaries are everywhere once you look: string lengths (password of 7, 8 characters), dates (the day before, on and after an expiry), quantities (0, 1, max stock), file sizes, pagination limits and time-outs. Try the Boundary Value Calculator to generate values for any range.
Decision table testing
Idea: when behaviour depends on combinations of conditions, list every combination in a table with the expected action. This exposes missing and contradictory rules.
Example: a shop’s shipping rules.
- Members get free shipping.
- Non-members get free shipping on orders of 50 or more.
- Express shipping always costs 10, even for members.
| Rule | R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8 |
|---|---|---|---|---|---|---|---|---|
| Member? | Y | Y | Y | Y | N | N | N | N |
| Order 50 or more? | Y | Y | N | N | Y | Y | N | N |
| Express? | Y | N | Y | N | Y | N | Y | N |
| Shipping cost | 10 | 0 | 10 | 0 | 10 | 0 | ? | standard |
Building the table surfaces a question the requirements did not answer: R7, a non-member with a small express order. Is it 10, or the standard fee plus 10? That gap is a finding before you have run a single test.
With three yes/no conditions there are 2 × 2 × 2 = 8 combinations. Where some conditions do not affect the outcome, columns can be collapsed (“Express = Y gives 10 regardless of the other two”), which reduces the number of tests while keeping every rule covered.
State transition testing
Idea: when a system behaves differently depending on its current state and the event that occurs, model it as states and transitions, then test the transitions, including invalid ones.
Example: an order lifecycle.
[Created] --pay--> [Paid] --ship--> [Shipped] --deliver--> [Delivered]
| |
cancel cancel (refund)
v v
[Cancelled] <---------
A state table makes invalid transitions explicit:
| Current state | pay | ship | cancel | deliver |
|---|---|---|---|---|
| Created | Paid | invalid | Cancelled | invalid |
| Paid | invalid | Shipped | Cancelled (refund) | invalid |
| Shipped | invalid | invalid | invalid | Delivered |
| Delivered | invalid | invalid | invalid | invalid |
| Cancelled | invalid | invalid | invalid | invalid |
Tests to derive:
- Every valid transition at least once (the simplest coverage level).
- Invalid transitions: can you ship a cancelled order? Cancel a delivered one? Pay twice? These are frequent sources of serious bugs, especially when the UI hides a button but the API still accepts the request.
Pairwise testing
Idea: many defects are triggered by the interaction of two parameters rather than three or more. Pairwise (all-pairs) testing picks a small set of combinations in which every pair of values appears at least once.
Example: a checkout tested across browser (Chrome, Firefox, Safari), payment (card, PayPal, voucher) and user type (guest, member). All combinations: 3 × 3 × 2 = 18. A pairwise set covers every pair in 9 tests:
| # | Browser | Payment | User |
|---|---|---|---|
| 1 | Chrome | Card | Guest |
| 2 | Chrome | PayPal | Member |
| 3 | Chrome | Voucher | Guest |
| 4 | Firefox | Card | Member |
| 5 | Firefox | PayPal | Guest |
| 6 | Firefox | Voucher | Member |
| 7 | Safari | Card | Guest |
| 8 | Safari | PayPal | Member |
| 9 | Safari | Voucher | Member |
Check: every browser meets every payment once, and every browser and every payment meets both user types at least once. The savings grow quickly with more parameters. Generate sets with the Pairwise Generator.
Experience-based techniques
Formal techniques miss defects that experience catches. Use them alongside, not instead.
- Error guessing: anticipate likely mistakes from experience. Common targets: empty and whitespace-only input, very long strings, Unicode and emoji in names, leading zeros, time zones and daylight-saving changes, double-clicking submit, browser back after payment, two tabs editing the same record.
- Checklist-based testing: maintain checklists from past defects and standards (accessibility, security basics, form behaviour).
- Exploratory testing: simultaneous learning, test design and execution, guided by a charter such as “Explore discount codes with unusual cart contents to discover pricing errors” and time-boxed to an hour or so.
Choosing techniques
| If the feature has… | Start with |
|---|---|
| Input ranges and fields | Equivalence partitioning + boundary value analysis |
| Business rules with combined conditions | Decision tables |
| Workflows, statuses, lifecycles | State transition testing |
| Many configuration options or environments | Pairwise testing |
| Unclear specs, new or risky areas | Exploratory testing and error guessing |
Most real features need two or three techniques together.
Practice exercise
A registration form has these rules:
- Username: 4 to 16 characters, letters and digits only.
- Age: whole number, 13 or older.
- Country: one of US, UK, IN; users from IN under 18 need a parent’s email.
Produce:
- Equivalence partitions and boundary values for username length and age.
- A decision table for the country/age/parent-email rule.
- Three error-guessing tests.
Use the Boundary Value Calculator to check your boundaries and the Test Case Generator to write them up. A good answer separates valid and invalid partitions, includes both sides of every boundary, and finds at least one gap in the rules.
Show a model answer
Username length: partitions are 0 (empty), 1–3 (too short), 4–16 (valid), 17+ (too long). Character partitions: letters and digits (valid), containing a space, symbol or non-ASCII letter (invalid). Boundaries (three-value): 3, 4, 5 and 15, 16, 17.
Age: partitions are below 13 (invalid), 13+ (valid), non-integer, non-numeric, empty. Boundaries: 12, 13, 14. For the IN rule, add 17, 18, 19.
Decision table
| Rule | R1 | R2 | R3 | R4 |
|---|---|---|---|---|
| Country is IN? | Y | Y | Y | N |
| Age under 18? | Y | Y | N | (any) |
| Parent email given? | Y | N | (any) | (any) |
| Result | Accept | Reject: parent email required | Accept | Accept |
Gaps found: Is there a maximum age? Is the parent email validated, and can it equal the user’s own email? Is “Ana_1” rejected (underscore) and is “ÁnaBeta” rejected (non-ASCII letter)?
Error guessing
- Username with leading or trailing spaces, such as “ ana1 “.
- Username that differs only by case from an existing one (“Ana1” vs “ana1”).
- Age entered as “013” or “13.0”.
Key takeaways
- Test design techniques turn an infinite input space into a small, defensible set of tests.
- Equivalence partitioning picks one value per class; boundary value analysis tests the edges, where comparison bugs live.
- Decision tables cover combinations of conditions and expose missing rules; state transition testing covers workflows, including invalid transitions.
- Pairwise testing covers every pair of parameter values with far fewer tests, but not higher-order interactions.
- Combine formal techniques with error guessing and exploratory testing.
Key terms
Finished this lesson?
Progress is saved in this browser. No account needed.