Test Case Design Foundations
Review design heuristics that keep test suites focused yet comprehensive — ISTQB Foundation Level §4 | Chowdhury Chapter 4 & 9
Learning Objectives
- Explain why well-designed test cases are the prerequisite for an efficient, manageable test suite.
- Identify and apply the key attributes that make a test case focused and unambiguous.
- Describe the three heuristic families: black-box, white-box, and experience-based.
- Apply Equivalence Partitioning, Boundary Value Analysis, Decision Tables, and State Transition heuristics to design representative test cases.
- Apply statement, branch, and path coverage criteria as white-box design heuristics.
- Recognise and avoid common test case design anti-patterns that cause suite bloat and redundancy.
Why Test Case Design Matters
Before a test suite can be minimized, prioritized, or selected (all topics of Module 5), its individual test cases must be well designed. A poorly designed test suite will:
Redundant cases are added because earlier ones were not clearly scoped, leading to uncontrolled growth.
Random or intuition-driven cases cluster around happy paths, leaving boundary conditions and error paths untested.
Ambiguous expected results make it unclear whether a test failure is a product defect or an outdated expectation.
Duplicate cases consume execution time repeatedly without adding coverage value.
| Session | Topic | Relationship to Design |
|---|---|---|
| 5.1 (this session) | Test Case Design Foundations | Upstream: prevent bloat and gaps at creation time |
| 5.2 | Why Test Suites Grow | Diagnose: identify what causes suites to expand |
| 5.3 | Test Suite Minimization | Repair: remove redundancy after the fact |
| 5.4 | Benefits of Minimization | Justify: quantify the gains |
| 5.5 – 5.7 | Prioritization & Metrics | Optimise: order execution for maximum early defect detection |
Anatomy of a Well-Formed Test Case
A test case is a documented specification of a single executable test scenario. Each attribute has a precise purpose in keeping the suite focused and unambiguous:
<MODULE>-<NUMBER> e.g. AUTH-042.
auth_token is set with HttpOnly flag" is.
| Attribute | Poorly Formed | Well Formed |
|---|---|---|
| Objective | Test login | Verify that a registered user with correct credentials is authenticated and redirected to the dashboard |
| Test Data | Valid username and password | username: alice@test.com, password: P@ssw0rd! |
| Expected Result | Login should succeed | HTTP 200; redirect to /dashboard; auth_token cookie set; user name "Alice" displayed in header |
| Traceability | (none) | REQ-AUTH-001, US-12 |
The Focused vs. Comprehensive Tension
Good test case design must simultaneously satisfy two goals that can pull in opposite directions:
Focused
- Each case tests exactly one aspect of behaviour.
- Minimal overlap with other cases in the suite.
- Fast to execute; easy to diagnose when it fails.
- Reduces redundancy and keeps the suite small.
Comprehensive
- Together, cases cover all significant input partitions.
- Boundary values and error paths are explicitly represented.
- Every requirement and risk item has at least one trace.
- No major defect type escapes the suite entirely.
Heuristic Families Overview
Test case design heuristics fall into three families, each exploiting different knowledge about the system under test:
| Family | Knowledge Source | Key Heuristics | When to Apply |
|---|---|---|---|
| Black-Box (Specification-based) |
Requirements, user stories, specs — no access to source code needed | Equivalence Partitioning, Boundary Value Analysis, Decision Tables, State Transition, Use Case Testing | Always; the primary design approach at all test levels |
| White-Box (Structure-based) |
Source code, control flow, data flow — requires code visibility | Statement Coverage, Branch Coverage, Path Coverage, Condition Coverage, MC/DC | Unit and component testing; when coverage gaps in black-box cases must be closed |
| Experience-Based | Tester expertise, defect history, domain knowledge | Error Guessing, Exploratory Testing, Checklist-Based Testing | Supplement formal heuristics; fill gaps that spec and code analysis miss |
Black-Box Design Heuristics
1. Equivalence Partitioning (EP)
Principle: Divide the input domain into equivalence classes — groups of values that are expected to be processed identically by the system. One representative from each class is sufficient. Testing more values from the same class adds no information.
Heuristic rule: For each input variable or combination of variables, identify:
- Valid partitions — values the system should accept and process correctly.
- Invalid partitions — values the system should reject with an appropriate error.
Suite discipline: Select exactly one representative from each partition. If you find yourself selecting two values from the same class "just to be safe", you are adding redundancy, not coverage.
| Partition | Range | Type | Representative Value |
|---|---|---|---|
| Child | 1 – 12 | Valid | 8 |
| Teen | 13 – 17 | Valid | 15 |
| Adult | 18 – 64 | Valid | 35 |
| Senior | 65 – 120 | Valid | 70 |
| Zero / Negative | ≤ 0 | Invalid | 0, −5 |
| Exceeds maximum | ≥ 121 | Invalid | 150 |
| Non-integer | Decimal, string | Invalid | 17.5, "abc" |
Result: 7 partitions → 7 test cases cover the entire age domain. Without EP, a tester might write 50 cases testing age 1, 2, 3… providing no additional defect-finding value.
2. Boundary Value Analysis (BVA)
Principle: Defects cluster at boundaries between partitions — the classic "off-by-one" error. BVA supplements EP by adding test cases at the exact boundary and just beyond it on each side.
Heuristic rule: For each boundary between a valid and invalid partition, test:
- On-point: The exact boundary value itself.
- Off-point (just inside): One step inside the valid range.
- Off-point (just outside): One step outside the valid range (into invalid territory).
| BVA Point | Value | Expected Result | Why |
|---|---|---|---|
| Just below lower bound | 5 chars | Reject: too short | Off-point (invalid side) |
| Lower bound | 6 chars | Accept | On-point (minimum valid) |
| Just above lower bound | 7 chars | Accept | Off-point (valid side) |
| Just below upper bound | 19 chars | Accept | Off-point (valid side) |
| Upper bound | 20 chars | Accept | On-point (maximum valid) |
| Just above upper bound | 21 chars | Reject: too long | Off-point (invalid side) |
Focused design discipline: BVA gives 6 targeted cases for this field. These 6 cases replace dozens of arbitrary length-test cases that would add no additional defect-finding value.
3. Decision Table Testing
Principle: When system behaviour depends on combinations of conditions, a decision table systematically enumerates all relevant combinations — preventing the common error of testing only one condition at a time.
Heuristic rule: List all conditions (Boolean inputs). Create one column per significant combination. Assign actions (expected results) to each combination. This gives the minimum set of cases to cover all decision logic.
Conditions: (C1) Credit score ≥ 700? (C2) Income sufficient? (C3) Existing debt < 30%?
| TC-1 | TC-2 | TC-3 | TC-4 | TC-5 | |
|---|---|---|---|---|---|
| C1: Credit ≥ 700 | Y | Y | Y | N | N |
| C2: Income sufficient | Y | Y | N | Y | N |
| C3: Debt < 30% | Y | N | Y | Y | N |
| Action: Approve | Yes | No | No | No | No |
| Action: Refer to officer | No | Yes | Yes | Yes | No |
| Action: Reject | No | No | No | No | Yes |
Suite benefit: Without a decision table, a tester might write 3 cases (one per condition) and miss the critical combination interactions. The table guarantees combinatorial completeness with 5 focused cases.
4. State Transition Testing
Principle: For systems with state (e.g., login sessions, order workflows, device modes), defects often occur at transitions between states, especially invalid transitions. Design test cases to cover both valid and invalid transitions.
Heuristic rule: Build a State Transition Diagram (or table). Coverage criteria:
- 0-switch coverage: Cover every valid state at least once.
- 1-switch coverage: Cover every valid transition (state + event → state) at least once.
- Invalid transition testing: Attempt transitions that should be rejected (e.g., paying for an already-cancelled order).
| Current State | Event | Next State | Test Case |
|---|---|---|---|
| Created | Pay | Paid | TC-ORD-01: Create order → pay → verify state is "Paid" |
| Created | Cancel | Cancelled | TC-ORD-02: Create order → cancel → verify state is "Cancelled" |
| Paid | Ship | Shipped | TC-ORD-03: Paid order → ship → verify state is "Shipped" |
| Shipped | Deliver | Delivered | TC-ORD-04: Shipped order → deliver → verify state is "Delivered" |
| Cancelled | Pay | — (invalid) | TC-ORD-05: Cancelled order → attempt pay → verify error "Cannot pay cancelled order" |
| Delivered | Ship | — (invalid) | TC-ORD-06: Delivered order → attempt ship → verify error "Order already delivered" |
White-Box Design Heuristics
White-box heuristics use the source code structure to identify test cases that exercise code paths not covered by specification-based techniques. They are primarily used at unit and component test level.
Statement Coverage (SC)
Criterion: Every executable statement must be executed by at least one test case.
Design heuristic: After writing black-box cases, run coverage analysis. For each uncovered statement, ask: what input is required to reach this statement? Add a targeted case.
SC-driven additions: TC-PAY-ERR-01 (amount=0) and TC-PAY-CRYPTO-01 (method="crypto") close the gaps identified by coverage analysis.
Branch Coverage (BC)
Criterion: Every decision point (if/else, switch, loop condition) must be exercised with both the true and false outcome at least once. BC subsumes SC (100% BC implies 100% SC).
Design heuristic: For every if statement, ensure you have one test where the condition evaluates true AND one where it evaluates false. For loops, test: zero iterations, one iteration, multiple iterations.
| Loop Scenario | Why it matters |
|---|---|
| 0 iterations (loop not entered) | Tests initialisation and skip logic; common source of defects |
| 1 iteration | Tests the loop body executes once; catches off-by-one on first pass |
| Typical n iterations | Tests normal execution behaviour |
| Maximum iterations | Tests upper bound and termination condition; catches infinite-loop risks |
Path Coverage (PC) & MC/DC
Path Coverage: Every possible path through the control flow graph is executed. Provides the strongest structural guarantee but is often impractical (the number of paths grows exponentially with nested conditions).
Modified Condition/Decision Coverage (MC/DC): A practical alternative used in safety-critical systems (DO-178C, IEC 61508). Each condition in a compound decision must independently affect the outcome. Stronger than branch coverage, far more practical than full path coverage.
(A and B) or C
| Test | A | B | C | Result | Purpose |
|---|---|---|---|---|---|
| T1 | T | T | F | T | A independently affects result (vary A, fix B=T, C=F) |
| T2 | F | T | F | F | A’s effect confirmed |
| T3 | T | F | F | F | B independently affects result |
| T4 | F | F | T | T | C independently affects result |
| T5 | F | F | F | F | C’s effect confirmed |
5 cases to achieve MC/DC vs. 8 cases for exhaustive combinatorial coverage — a 37.5% reduction for this condition alone.
Path Coverage ⊃ MC/DC ⊃ Branch Coverage ⊃ Statement Coverage
A higher-level criterion subsumes lower ones: achieving MC/DC guarantees you also achieve branch and statement coverage. For most commercial software, branch coverage (100% BC) is the standard target; MC/DC is required in safety-critical domains.
Experience-Based Design Heuristics
Specification and code analysis are systematic but cannot anticipate every failure mode. Experience-based heuristics leverage domain knowledge, defect history, and tester intuition to fill the remaining gaps.
Error Guessing
Principle: Experienced testers can predict where defects are likely to hide based on common programming mistakes, past defect patterns, and domain-specific risk areas.
Heuristic checklist for error guessing:
What happens when a required field is empty? When a collection has zero elements? When a string is null vs. empty?
Integer overflow, floating-point precision, implicit type conversion (e.g., "10" + 5 = "105" in weakly typed languages).
Two users modifying the same record simultaneously. Race conditions on shared resources. Deadlock scenarios.
File upload at exactly max size. Database connection pool exhaustion. Memory allocation near limits.
Apostrophes in names (SQL injection risk). Unicode and emoji in text fields. Newlines in address fields.
Date calculations spanning daylight-saving time transitions. Leap year (Feb 29). Different timezone offsets.
Exploratory Testing
Principle: Simultaneously design, execute, and evaluate test cases in real time. Not ad-hoc — guided by a charter that scopes the session.
Charter format: Explore [target] using [resources] to discover [information].
Example charter: "Explore the payment gateway integration using a test card that triggers a bank timeout to discover whether the system handles partial authorisation states correctly."
Suite contribution: Exploratory findings that reveal reproducible defects are then converted into regression test cases with full specification (ID, preconditions, steps, expected result). Exploration generates; regression formalises.
Checklist-Based Testing
Principle: Use curated checklists of known defect types for a domain or technology to ensure important risk areas are not forgotten.
- All user inputs sanitised before use in SQL queries (SQL injection prevention)
- Output encoding applied before rendering user content in HTML (XSS prevention)
- Authentication tokens expire after inactivity (session management)
- Sensitive data not transmitted in URL parameters (information exposure)
- HTTPS enforced on all authenticated endpoints (transport security)
- CSRF tokens present on all state-changing forms (CSRF prevention)
Combining Heuristics for a Focused, Comprehensive Suite
No single heuristic achieves both goals alone. The disciplined combination below is the recommended design process:
Define the input space. One representative per partition + boundary cases. This gives the minimum specification-driven set.
For logic-heavy behaviour or stateful workflows. Ensures combinatorial and transition coverage without case explosion.
Run coverage tools. Identify uncovered statements, branches, or conditions. Add minimal targeted cases for each gap.
Review defect history for this module. Apply domain-specific checklists. Add cases for high-risk error types not reached by Steps 1–3.
Check whether any two cases test identical code paths with the same expected result. Merge or remove confirmed duplicates before adding the cases to the suite.
- Every equivalence class has at least one representative → comprehensive
- No two cases test the same partition with the same expected result → focused
- Every branch in the code is covered → comprehensive
- Each case tests exactly one primary behaviour → focused
Test Case Design Anti-Patterns
Anti-patterns are common design mistakes that create bloated, fragile, or gap-ridden suites. Recognising them at design time prevents the suite management problems covered in Sessions 5.2–5.4.
Anti-Pattern: The Omnibus Test Case
One test case that checks 10 different things in sequence. When it fails, the failure message cannot pinpoint which of the 10 behaviours is broken.
Fix: One test case, one objective. Split along concern lines.
Corrected: Single-Responsibility Cases
10 separate cases, each with a clearly scoped objective. Failures immediately identify the broken behaviour.
Faster diagnosis; easier to exclude or select during regression.
Anti-Pattern: Happy-Path Only
All cases use valid, "nice" inputs. Invalid partitions, boundary values, and error conditions are untested. Defects in error handling are missed entirely.
Fix: Explicitly apply EP to identify and cover invalid partitions.
Corrected: Partition-Complete Coverage
At least one case per valid AND invalid equivalence class. Error handling is explicitly tested.
Defects in input validation and error paths are exposed before production.
Anti-Pattern: Vague Expected Results
"The system should display an error message." Which message? With what content? For how long? Pass/fail is subjective and varies between testers.
Fix: Specify the exact message text, HTTP status code, UI element state.
Corrected: Precise Expected Results
"Toast notification with text 'Password must be 6–20 characters' is displayed for 3 seconds. Field border turns red. Submit button remains disabled."
Automatable; consistent between manual and automated execution.
Anti-Pattern: Arbitrary Data Clones
TC-LOGIN-01 uses alice@test.com; TC-LOGIN-02 uses bob@test.com; TC-LOGIN-03 uses carol@test.com — all testing the same happy-path login with different but equivalent users.
Fix: One representative per equivalence class. Additional data-driven variation adds zero coverage value.
Corrected: One Representative Per Class
TC-LOGIN-01 with one representative valid user covers the entire valid-credentials partition.
Suite stays compact; minimization effort is reduced at the source.
Anti-Pattern: Missing Traceability
Test cases have no link to requirements or user stories. When a requirement changes, it is impossible to identify which tests must be updated. When a test fails, it is unclear what business requirement is violated.
Fix: Every test case references at least one requirement ID or user story.
Corrected: Full Traceability
Each case references REQ-XXX or US-XXX. Change impact analysis becomes a database query: "which tests trace to the changed requirement?"
Regression selection in Sessions 4.6–4.7 is only practical with this discipline in place.
Worked Example: Designing a Focused, Comprehensive Suite
Feature under test: Discount coupon validation in an e-commerce checkout system.
Specification: A coupon code is valid if: (a) it exists in the system, (b) it has not expired, and (c) the cart subtotal meets the minimum purchase threshold. A valid coupon reduces the total by a fixed percentage (5%–50%). Invalid or expired coupons display an error message.
| Dimension | Valid Partition(s) | Invalid Partition(s) |
|---|---|---|
| Coupon existence | Exists in system | Does not exist (typo / unknown code) |
| Expiry | Expiry date ≥ today | Expiry date < today (expired) |
| Cart subtotal vs threshold | Subtotal ≥ minimum threshold | Subtotal < minimum threshold |
| Discount % | 5, 10, 25, 50 (valid range) | <5% or >50% (data integrity; test via boundary) |
| Boundary | Test Value | Expected Result |
|---|---|---|
| Expiry: today is boundary | Coupon expires today (same date) | Accept: coupon is still valid on expiry date |
| Expiry: yesterday | Coupon expired yesterday | Reject: "Coupon has expired" |
| Cart at exactly minimum threshold | Cart = $50.00, threshold = $50.00 | Accept: minimum met |
| Cart one cent below threshold | Cart = $49.99, threshold = $50.00 | Reject: "Minimum purchase not met" |
| Discount at 5% (lower bound) | Coupon with 5% discount | Accept: correct calculation |
| Discount at 50% (upper bound) | Coupon with 50% discount | Accept: correct calculation |
| TC-D1 | TC-D2 | TC-D3 | TC-D4 | TC-D5 | |
|---|---|---|---|---|---|
| Coupon exists? | Y | Y | Y | N | Y |
| Not expired? | Y | Y | N | — | Y |
| Threshold met? | Y | N | — | — | Y (boundary) |
| Action | Apply discount | Reject: threshold | Reject: expired | Reject: invalid code | Apply: boundary OK |
- Apply coupon twice to the same cart — should only apply once.
- Coupon code with leading/trailing whitespace: "SAVE10 " — should the system trim it?
- Coupon code in lowercase "save10" when stored as "SAVE10" — case sensitivity?
- Apply coupon after a cart item is removed (cart subtotal drops below threshold mid-session).
Total cases designed: ~16 cases (4 EP + 6 BVA + 5 decision table + 4 error guessing).
Without heuristics, a team might write 50+ cases testing random coupon codes with no systematic coverage, while still missing the threshold boundary and the whitespace error-guessing scenario.
Test Case Design Quality Checklist
Use this checklist before adding test cases to the suite to prevent anti-patterns and ensure each case earns its place:
| # | Quality Check | Why it Matters |
|---|---|---|
| 1 | Does this case have exactly one clearly stated objective? | Prevents omnibus cases; enables precise failure diagnosis |
| 2 | Does this case represent a partition or boundary not already covered by another case? | Prevents arbitrary-data-clone redundancy |
| 3 | Are preconditions fully specified (data state, system state, user role)? | Prevents flaky, environment-dependent failures |
| 4 | Is the expected result precise enough to be verified by an automated assertion? | Prevents subjective pass/fail judgements |
| 5 | Is the test data specific (exact values, not "valid input")? | Ensures reproducibility across executions and environments |
| 6 | Does the case have a traceability link to a requirement or risk item? | Enables impact analysis for regression selection |
| 7 | Are postconditions defined (clean-up steps for shared resources/data)? | Prevents test-order dependencies and data contamination |
| 8 | Does removing this case reduce coverage (partition, branch, or risk)? | If no — the case is a candidate for immediate removal |
Common Mistakes in Test Case Design
EP selects one representative from the middle of a class. BVA targets the edges between classes. Both are needed — EP without BVA misses boundary defects; BVA without EP misses mid-range behaviour.
Coverage is a measurement, not a design technique. Designing cases purely to hit 100% branch coverage can produce cases with no meaningful expected result. Design for requirements first; use coverage to find gaps.
Input validation defects are among the most exploited security vulnerabilities. Invalid partition cases test error handling, and their absence creates both quality and security gaps.
Cases that share test data become order-dependent. A case that modifies shared data corrupts the state for later cases. Each case should own or restore its own data.
Exploratory testing finds defects; it does not replace structured cases. When error guessing reveals a defect, a formal case must be written and added to the regression suite to prevent recurrence.
Every redundant case added today is a case that will need to be removed during minimization (Session 5.3). Redundancy prevention at design time is far cheaper than redundancy removal after the fact.
Class Activity
- Student ID: Must be a 10-digit number starting with the 4-digit year (2020–2026) followed by 6 digits.
- Email: Must end in
@vitap.ac.in. Max 60 characters total. - Date of Birth: User must be 17–30 years old at time of registration.
- Programme: One of: B.Tech, M.Tech, MBA, PhD.
- Password: 8–24 characters; must contain at least one uppercase letter, one digit, and one special character.
- Apply Equivalence Partitioning to all five fields. Create a partition table listing valid and invalid classes for each. Count the minimum number of cases this generates.
- Apply Boundary Value Analysis to Student ID (year boundary), Date of Birth (age boundary), and Password (length boundary). List the on-point, off-point (valid side), and off-point (invalid side) cases for each.
- Design a Decision Table for password complexity: treat the three complexity conditions (uppercase present, digit present, special character present) as Boolean conditions. How many combinations need testing?
- Apply error guessing to the Email field: list 5 error-guessing test cases beyond what EP and BVA would generate (e.g., special characters, international domains).
- Review your full case list. Identify any redundant cases (same partition, same expected result). Remove them and report the final case count vs. the initial count before review.
- 2 marks: EP table is complete (all five fields, both valid and invalid partitions).
- 2 marks: BVA cases correctly identify on-point and both off-points for all three fields.
- 2 marks: Decision table is structurally correct and all combinations are covered.
- 2 marks: Error guessing cases are genuinely beyond EP/BVA scope and well-specified.
- 2 marks: Redundancy review is performed and justified; final count is lower than initial count.
Exit Ticket
- A numeric age field accepts values 0–150. Using EP, identify all equivalence classes. Using BVA, identify the boundary test values. How many total test cases do EP + BVA together produce?
- A function has the code:
if (x > 0 and y != null). What is the minimum number of test cases required for (a) branch coverage and (b) MC/DC? Explain the difference. - A team achieves 95% branch coverage with their black-box test cases. A tester then adds 20 additional cases purely to reach 100% branch coverage. Are these 20 cases well-designed? What question should the tester ask before adding each one?
- Name two design anti-patterns that directly cause test suite bloat (covered in Session 5.2). Explain how a design heuristic from this session prevents each one.
Summary & Assignment
Well-designed test cases are the upstream control for test suite efficiency. Black-box heuristics (EP, BVA, decision tables, state transitions) define the minimum representative set from the specification. White-box heuristics (statement, branch, MC/DC) identify structural gaps. Experience-based heuristics (error guessing, exploratory charters, checklists) fill domain-specific risks. Together, and with a redundancy review before cases enter the suite, they produce a suite that is simultaneously focused and comprehensive — the foundation that makes minimization, selection, and prioritization effective.
- A test case must have: unique ID, single objective, precise preconditions, deterministic steps, exact test data, verifiable expected result, and a traceability link.
- EP divides the input domain into classes; one representative per class. BVA targets boundaries between classes. Together they cover the specification space systematically.
- Decision tables handle combinatorial logic; state transition testing handles workflow and mode-based behaviour.
- Branch coverage subsumes statement coverage. MC/DC subsumes branch coverage. Higher criteria are used for safety-critical domains.
- Error guessing and exploratory testing supplement formal heuristics with domain expertise and defect history.
- Anti-patterns (omnibus cases, happy-path only, vague expected results, data clones) create bloat and gaps — preventing them at design time is cheaper than fixing them with minimization later.
- Select one module from your mini-project (minimum 3 functions / 1 user story). Apply EP and BVA to design a focused, specification-driven test case set. Document each case with all 8 attributes from the anatomy checklist.
- Run your EP+BVA cases through a coverage tool. Report the resulting statement and branch coverage. For each branch not covered, add a targeted white-box case and explain which structural gap it closes.
- Apply error guessing to your module: write 5 error-guessing cases based on the defect history (git blame / issue tracker) of your project or a domain-specific risk checklist.
- Apply the 8-point quality checklist to your complete case set. Identify any cases that fail one or more checks. Revise or remove them. Report the before/after case count.
- For each case in your final set, add a traceability link to the corresponding requirement or user story. Confirm that every requirement has at least one case and no case exists without a requirement link.