Combining Equivalence Partitioning and BVA — A Unified Test Design Workflow

Chapter 10 introduced Equivalence Partitioning and Boundary Value Analysis as individual techniques. In practice, you never use them in isolation — they form a two-step workflow where EP divides the input space into partitions and BVA selects the most defect-prone values from each partition. Mastering this combined workflow is what transforms a list of requirements into a lean, high-coverage test suite that catches the maximum number of defects with the minimum number of test cases.

The EP + BVA Workflow — Step by Step

For any input field, follow this sequence: identify partitions (EP), then select boundary values within each partition (BVA), then add special-case values for non-standard inputs. The result is a prioritised list of test values that covers every distinct behaviour and probes every known defect hotspot.

# Combined EP + BVA workflow for a product quantity field
# Requirement: Quantity must be 1-999. Free shipping for orders of 100+.

# ── Step 1: Equivalence Partitioning ──
partitions = [
    {"id": "EP1", "range": "qty < 1",        "valid": False, "behaviour": "Error: minimum 1"},
    {"id": "EP2", "range": "1 <= qty <= 99",  "valid": True,  "behaviour": "Standard shipping ($5)"},
    {"id": "EP3", "range": "100 <= qty <= 999","valid": True, "behaviour": "Free shipping"},
    {"id": "EP4", "range": "qty > 999",       "valid": False, "behaviour": "Error: maximum 999"},
    {"id": "EP5", "range": "empty input",     "valid": False, "behaviour": "Error: required field"},
    {"id": "EP6", "range": "non-numeric",     "valid": False, "behaviour": "Error: enter a number"},
    {"id": "EP7", "range": "decimal (e.g. 5.5)","valid": False,"behaviour": "Error: whole numbers only"},
    {"id": "EP8", "range": "negative number", "valid": False, "behaviour": "Error: minimum 1"},
]

# ── Step 2: BVA — select boundary values for numeric partitions ──
boundary_values = [
    # EP1 ↔ EP2 boundary (lower edge)
    {"value": 0,   "partition": "EP1/EP2", "type": "Boundary", "expected": "Error: minimum 1"},
    {"value": 1,   "partition": "EP2",     "type": "Boundary", "expected": "Standard shipping"},
    {"value": 2,   "partition": "EP2",     "type": "Boundary", "expected": "Standard shipping"},
    # EP2 ↔ EP3 boundary (shipping tier change)
    {"value": 99,  "partition": "EP2",     "type": "Boundary", "expected": "Standard shipping ($5)"},
    {"value": 100, "partition": "EP3",     "type": "Boundary", "expected": "Free shipping"},
    {"value": 101, "partition": "EP3",     "type": "Boundary", "expected": "Free shipping"},
    # EP3 ↔ EP4 boundary (upper edge)
    {"value": 998, "partition": "EP3",     "type": "Boundary", "expected": "Free shipping"},
    {"value": 999, "partition": "EP3",     "type": "Boundary", "expected": "Free shipping"},
    {"value": 1000,"partition": "EP4",     "type": "Boundary", "expected": "Error: maximum 999"},
]

# ── Step 3: Representative values for non-numeric partitions ──
special_values = [
    {"value": "",      "partition": "EP5", "type": "Special", "expected": "Error: required field"},
    {"value": "abc",   "partition": "EP6", "type": "Special", "expected": "Error: enter a number"},
    {"value": "5.5",   "partition": "EP7", "type": "Special", "expected": "Error: whole numbers only"},
    {"value": -3,      "partition": "EP8", "type": "Special", "expected": "Error: minimum 1"},
]

all_tests = boundary_values + special_values

print("Combined EP + BVA — Product Quantity Field")
print("=" * 70)
print(f"\nStep 1: {len(partitions)} equivalence partitions identified")
print(f"Step 2: {len(boundary_values)} boundary values selected")
print(f"Step 3: {len(special_values)} special-case values added")
print(f"Total:  {len(all_tests)} test values\n")

print(f"{'Value':<8} {'Partition':<12} {'Type':<10} {'Expected Result'}")
print("-" * 70)
for t in all_tests:
    print(f"{str(t['value']):<8} {t['partition']:<12} {t['type']:<10} {t['expected']}")
Note: The combined workflow produces tests at three levels of priority. Boundary values are highest priority — they target the exact points where defects cluster. Representative partition values are medium priority — they verify that each distinct behaviour works for at least one input. Special-case values are essential additions — they catch type-handling defects that neither EP nor BVA alone would surface. When time is limited, execute in this priority order to maximise defect detection with the tests you can fit.
Tip: Notice that the EP + BVA workflow identified the shipping tier boundary (99/100/101) as well as the field validation boundaries (0/1 and 999/1000). Requirements often contain multiple boundaries within a single field — the data type boundary, the valid range boundary, and business rule boundaries. A thorough analysis considers all three layers. Miss the business rule boundary and you might never test whether free shipping kicks in at exactly 100 items.
Warning: Do not apply BVA mechanically without understanding what each boundary represents. The boundary between 99 and 100 items has business significance (shipping cost changes), while the boundary between 50 and 51 items has none. Both deserve testing, but the business-significant boundary deserves deeper testing — including verification that the shipping cost display updates, that the order summary reflects the change, and that the backend calculation matches the frontend display.

Common Mistakes

Mistake 1 — Applying EP without following up with BVA

❌ Wrong: Selecting one representative value from each partition (e.g., 50 for the valid range) and stopping. No boundary values are tested.

✅ Correct: Using EP to identify partitions, then applying BVA to select values at every boundary between partitions. The representative value 50 confirms the partition works; the boundary values 99, 100, 101 catch the off-by-one errors that hide at the edges.

Mistake 2 — Forgetting non-numeric partitions for numeric fields

❌ Wrong: Applying EP only to the numeric range (below min, valid, above max) and not considering empty input, text input, special characters, or decimal numbers.

✅ Correct: Including partitions for every input type the user could enter — empty, text, decimals, negative numbers, extremely large numbers, and special characters. These "type error" partitions frequently reveal unhandled exceptions.

🧠 Test Yourself

When applying the combined EP + BVA workflow to a numeric field, in what order should the three types of test values be prioritised if testing time is limited?