Testing every possible input to a system is impossible — a single text field that accepts 100 characters has more possible combinations than atoms in the universe. Equivalence Partitioning (EP) solves this by dividing the infinite input space into a manageable number of groups — called partitions or classes — where all values within a group are expected to produce the same behaviour. You then test one representative value from each partition, confident that if it works for one value in the class, it works for all of them.
Equivalence Partitioning — Maximum Coverage, Minimum Tests
The core principle is simple: if the system treats all values in a range the same way, testing one value from that range is sufficient. This dramatically reduces the number of test cases while maintaining coverage of every distinct behaviour.
# Equivalence Partitioning applied to an age field
# Requirement: Age must be 18-65 for standard insurance policy
# Age < 18 → "Underage — not eligible"
# Age 18-65 → "Eligible — standard policy"
# Age > 65 → "Senior — requires supplementary review"
# Step 1: Identify equivalence partitions
partitions = [
{"id": "EP1", "range": "age < 18", "class": "Invalid (underage)",
"expected": "Not eligible", "valid": False},
{"id": "EP2", "range": "18 <= age <= 65", "class": "Valid (standard)",
"expected": "Eligible — standard policy", "valid": True},
{"id": "EP3", "range": "age > 65", "class": "Invalid (senior)",
"expected": "Requires supplementary review", "valid": False},
]
# Step 2: Select ONE representative value from each partition
test_values = [
{"partition": "EP1", "value": 10, "expected": "Not eligible"},
{"partition": "EP2", "value": 35, "expected": "Eligible — standard policy"},
{"partition": "EP3", "value": 70, "expected": "Requires supplementary review"},
]
# Additional partitions for non-numeric input
extra_partitions = [
{"id": "EP4", "range": "empty input", "class": "Invalid (missing)",
"expected": "Age is required", "value": ""},
{"id": "EP5", "range": "non-numeric", "class": "Invalid (type error)",
"expected": "Please enter a number", "value": "abc"},
{"id": "EP6", "range": "negative number", "class": "Invalid (negative)",
"expected": "Age must be a positive number", "value": -5},
{"id": "EP7", "range": "decimal number", "class": "Invalid (not integer)",
"expected": "Please enter a whole number", "value": 25.5},
]
print("Equivalence Partitioning — Age Field")
print("=" * 65)
print(f"\n{'Partition':<6} {'Range':<20} {'Class':<22} {'Test Value':>10}")
print("-" * 65)
for p in partitions:
tv = [t for t in test_values if t['partition'] == p['id']]
val = tv[0]['value'] if tv else "—"
print(f"{p['id']:<6} {p['range']:<20} {p['class']:<22} {str(val):>10}")
print(f"\nAdditional partitions (non-numeric inputs):")
for p in extra_partitions:
print(f" {p['id']}: {p['range']:<20} → Test: '{p['value']}' → {p['expected']}")
print(f"\nTotal: {len(partitions) + len(extra_partitions)} partitions → "
f"{len(partitions) + len(extra_partitions)} test cases")
print(f"Without EP: potentially millions of input combinations")
print(f"With EP: 7 representative tests covering all distinct behaviours")
Common Mistakes
Mistake 1 — Creating too few partitions by only considering valid and invalid
❌ Wrong: Two partitions — "valid" and "invalid" — with one test case each.
✅ Correct: Multiple partitions distinguishing between different types of invalid input (below range, above range, wrong data type, empty, special characters) because each type may trigger different error handling code.
Mistake 2 — Testing multiple values from the same partition
❌ Wrong: For the valid age range 18-65, testing with ages 20, 25, 30, 35, 40, 45 — six tests that all exercise the same code path.
✅ Correct: Testing with one representative value from the valid partition (e.g. 35) and using the saved effort to test values from other partitions (empty input, negative numbers, non-numeric text).