Test Design Techniques
Equivalence partitioning, boundary value analysis, decision tables and state transition testing with worked examples.
Why test design techniques matter
You can't test every input, so you need a systematic way to pick the few tests that find the most bugs. These four black-box techniques are used daily and asked about in nearly every QA interview.
Equivalence partitioning (EP)
Split inputs into groups (partitions) that the system should treat the same way, then test one value from each partition.
Requirement: an age field accepts 18 to 60.
| Partition | Range | Example test value | Expected |
|---|---|---|---|
| Invalid (too low) | ≤ 17 | 10 | Rejected |
| Valid | 18–60 | 35 | Accepted |
| Invalid (too high) | ≥ 61 | 75 | Rejected |
| Invalid (not a number) | text | "abc" | Rejected |
Four tests cover the whole input space instead of 100.
Boundary value analysis (BVA)
Bugs love edges — < instead of <= is one of the most common coding mistakes. BVA tests at and around each boundary.
For 18–60, test 17, 18, 19 and 59, 60, 61:
Value Expected
17 Rejected ← just below minimum
18 Accepted ← minimum
19 Accepted ← just above minimum
59 Accepted ← just below maximum
60 Accepted ← maximum
61 Rejected ← just above maximum
EP and BVA are used together: EP picks the partitions, BVA picks the edges.
You can see why BVA works by looking at a buggy implementation:
def is_eligible(age: int) -> bool:
return 18 < age <= 60 # bug: should be 18 <= age
for age in [17, 18, 19, 59, 60, 61]:
print(age, is_eligible(age))
# 18 False ← only the boundary test catches the bug
A test with age 35 would never find it.
Decision table testing
When the result depends on combinations of conditions, a decision table makes sure you cover every combination.
Requirement: free shipping if the order is over $50 or the customer is a member — but never for international orders.
| Rule | Order > $50 | Member | International | Free shipping? |
|---|---|---|---|---|
| R1 | Yes | Yes | No | Yes |
| R2 | Yes | No | No | Yes |
| R3 | No | Yes | No | Yes |
| R4 | No | No | No | No |
| R5 | Yes | Yes | Yes | No |
| R6 | Yes | No | Yes | No |
| R7 | No | Yes | Yes | No |
| R8 | No | No | Yes | No |
Three yes/no conditions give 2³ = 8 rules → 8 test cases. Writing the table often reveals gaps in the requirement itself ("what about members abroad?") before any code exists.
State transition testing
Use it when behaviour depends on what happened before — accounts, orders, workflows.
Requirement: a login is locked after 3 consecutive failed attempts; a correct password resets the count.
States: S0 (0 failures) → S1 (1) → S2 (2) → LOCKED
S0 --wrong--> S1 --wrong--> S2 --wrong--> LOCKED
S1 --correct--> logged in, back to S0
S2 --correct--> logged in, back to S0
LOCKED --correct--> still LOCKED (message shown)
Test cases come straight from the arrows:
- Wrong, wrong, wrong → account locked.
- Wrong, wrong, correct → logged in, counter reset.
- Wrong, correct, then wrong ×2 → not locked (counter was reset).
- Locked account + correct password → still locked.
Test 3 is the one most likely to find a bug — the reset path is often forgotten.
Experience-based techniques
Alongside the formal techniques, testers use experience:
- Error guessing — trying inputs that commonly break things: empty strings, very long text, emojis and Urdu/Arabic text, leading/trailing spaces, special characters, zero, negative numbers, dates like 29 February.
- Checklists — a team's list of things that broke before.
- Exploratory testing — structured, curious investigation.
Putting it together
For a discount-code field (codes are 6–12 uppercase letters/digits):
- EP: valid code, invalid characters, too short, too long, expired, already used.
- BVA: lengths 5, 6, 7, 11, 12, 13.
- Decision table: valid/expired × minimum order met/not met × first-time/used.
- Error guessing: lowercase input, spaces around the code, pasting with a newline.
That's roughly 20 well-chosen tests instead of hundreds of random ones.