Testing every possible input value is impossible. A simple password field that accepts 8-32 characters has billions of potential inputs. How do you test comprehensively without spending years writing test cases?
Enter Equivalence Partitioning (EP) — a fundamental test case design technique and black-box testing approach that divides input data into logical groups (partitions) where all values in a partition should behave the same way. By testing one representative value from each partition, you achieve broad coverage with minimal test cases.
The Core Concept
Equivalence Partitioning Principle: If one value from a partition works correctly, all values in that partition should work correctly. Conversely, if one value fails, all values in that partition should fail.
Instead of testing:
Age inputs: 5, 10, 15, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80...
You test:
Age partitions:
- Invalid (too young): Test value = 10
- Valid (adult): Test value = 40
- Invalid (too old): Test value = 75
Three test cases instead of dozens, with the same or better defect detection.
How to Apply Equivalence Partitioning
Step 1: Identify Input Conditions
List all inputs and their requirements:
Example: Ticket Booking System
Input: Number of tickets
Requirement: Must be between 1 and 10 (inclusive)
Step 2: Identify Equivalence Partitions
Divide the input space into valid and invalid partitions:
Partition Type | Range | Description |
---|---|---|
Invalid | < 1 | Too few tickets |
Valid | 1-10 | Acceptable range |
Invalid | > 10 | Too many tickets |
Step 3: Select Test Values
Choose one representative value from each partition:
Partition | Test Value | Expected Result |
---|---|---|
Invalid (< 1) | 0 | Error: “Minimum 1 ticket required” |
Valid (1-10) | 5 | Booking successful |
Invalid (> 10) | 15 | Error: “Maximum 10 tickets allowed” |
Result: 3 test cases cover all scenarios
Valid vs Invalid Partitions
Every input typically has:
Valid Partitions
- Inputs the system should accept
- Expected to produce successful results
- Often there’s one valid partition, but complex inputs may have multiple
Invalid Partitions
- Inputs the system should reject
- Expected to produce errors
- Usually multiple invalid partitions (below range, above range, wrong type, null, etc.)
Example: Email Field
Valid Partitions:
- ✅ Standard email: "user@example.com"
- ✅ Email with numbers: "user123@test.com"
- ✅ Email with subdomain: "user@mail.example.com"
Invalid Partitions:
- ❌ Missing @: "userexample.com"
- ❌ Missing domain: "user@"
- ❌ Missing local part: "@example.com"
- ❌ Multiple @: "user@@example.com"
- ❌ Empty string: ""
- ❌ Special chars: "user name@example.com"
- ❌ Too long: [email > 254 chars]
Test Strategy: Select 1-2 values from valid partitions, 1 value from each invalid partition.
EP Test Case Example
**Feature**: User Registration - Age Field
**Requirement**: Age must be 18-65 years
**Equivalence Partitions:**
| Partition ID | Partition Type | Range | Test Value | Expected Result |
|-------------|---------------|-------|-----------|----------------|
| EP1 | Invalid | Age < 18 | 15 | Error: "You must be 18 or older" |
| EP2 | Valid | 18 ≤ Age ≤ 65 | 30 | Registration successful |
| EP3 | Invalid | Age > 65 | 70 | Error: "Age limit exceeded" |
| EP4 | Invalid | Non-numeric | "abc" | Error: "Age must be a number" |
| EP5 | Invalid | Negative | -5 | Error: "Age must be positive" |
| EP6 | Invalid | Decimal | 25.5 | Error: "Age must be whole number" |
| EP7 | Invalid | Empty | "" | Error: "Age is required" |
**Total Test Cases**: 7 (instead of testing all ages 0-999)
Combining EP with Boundary Value Analysis (BVA)
Equivalence Partitioning and Boundary Value Analysis work perfectly together:
EP alone:
Valid partition (18-65): Test value = 30
EP + BVA:
Valid partition (18-65):
- Test value = 18 (lower boundary)
- Test value = 30 (mid-range)
- Test value = 65 (upper boundary)
Example: Discount Tiers
Requirement: Purchase discounts based on amount
- $0-$99: No discount
- $100-$499: 10% discount
- $500-$999: 15% discount
- $1000+: 20% discount
EP Partitions:
Partition | Range | EP Test Value | EP+BVA Test Values |
---|---|---|---|
Tier 1 | $0-$99 | $50 | $0, $50, $99 |
Tier 2 | $100-$499 | $300 | $100, $300, $499 |
Tier 3 | $500-$999 | $750 | $500, $750, $999 |
Tier 4 | $1000+ | $1500 | $1000, $1500, $5000 |
EP alone: 4 test cases EP + BVA: 12 test cases (more thorough)
Real-World Examples
Example 1: Password Validation
Requirements:
- Length: 8-20 characters
- Must contain: uppercase, lowercase, number, special char
- Cannot contain: username, common words
Equivalence Partitions:
Valid Partition:
EP-V1: Valid password meeting all criteria
Test: "MyP@ssw0rd"
Invalid Partitions:
EP-I1: Too short (< 8 chars)
Test: "Pass1!"
EP-I2: Too long (> 20 chars)
Test: "MyVeryLongP@ssw0rd123456"
EP-I3: Missing uppercase
Test: "myp@ssw0rd1"
EP-I4: Missing lowercase
Test: "MYP@SSW0RD1"
EP-I5: Missing number
Test: "MyP@ssword"
EP-I6: Missing special char
Test: "MyPassword1"
EP-I7: Contains username
Test: "JohnP@ssw0rd" (if username is "john")
EP-I8: Common word
Test: "Password123!"
EP-I9: Empty
Test: ""
EP-I10: Only spaces
Test: " "
Test Automation:
import pytest
@pytest.mark.parametrize("password,expected,reason", [
# Valid partition
("MyP@ssw0rd", True, "Valid password"),
# Invalid partitions
("Pass1!", False, "Too short"),
("MyVeryLongP@ssw0rd123456", False, "Too long"),
("myp@ssw0rd1", False, "Missing uppercase"),
("MYP@SSW0RD1", False, "Missing lowercase"),
("MyP@ssword", False, "Missing number"),
("MyPassword1", False, "Missing special char"),
("Password123!", False, "Common word"),
("", False, "Empty"),
])
def test_password_validation(password, expected, reason):
result = validate_password(password)
assert result == expected, f"Failed: {reason}"
Example 2: Shipping Cost Calculator
Business Rules:
- Weight-based shipping
- 0-5 lbs: $5
- 5.01-20 lbs: $10
- 20.01-50 lbs: $25
- Over 50 lbs: Contact for quote
- Invalid: Negative or zero weight
Equivalence Partitions:
Partition | Weight Range | Test Value | Expected Cost |
---|---|---|---|
EP-I1 | ≤ 0 | 0 | Error: “Invalid weight” |
EP-I2 | < 0 | -5 | Error: “Invalid weight” |
EP-V1 | 0.01 - 5 | 3 | $5 |
EP-V2 | 5.01 - 20 | 12 | $10 |
EP-V3 | 20.01 - 50 | 30 | $25 |
EP-V4 | > 50 | 75 | “Contact for quote” |
EP-I3 | Non-numeric | “abc” | Error: “Weight must be numeric” |
EP-I4 | Null | null | Error: “Weight required” |
Implementation Test:
describe('Shipping Cost Calculator - EP', () => {
test('EP-I1: Zero weight should be rejected', () => {
expect(() => calculateShipping(0)).toThrow('Invalid weight');
});
test('EP-I2: Negative weight should be rejected', () => {
expect(() => calculateShipping(-5)).toThrow('Invalid weight');
});
test('EP-V1: Weight 0.01-5 lbs should cost $5', () => {
expect(calculateShipping(3)).toBe(5);
});
test('EP-V2: Weight 5.01-20 lbs should cost $10', () => {
expect(calculateShipping(12)).toBe(10);
});
test('EP-V3: Weight 20.01-50 lbs should cost $25', () => {
expect(calculateShipping(30)).toBe(25);
});
test('EP-V4: Weight over 50 lbs should require quote', () => {
expect(calculateShipping(75)).toBe('Contact for quote');
});
test('EP-I3: Non-numeric weight should be rejected', () => {
expect(() => calculateShipping('abc')).toThrow('Weight must be numeric');
});
});
Example 3: File Upload
Requirements:
- Allowed types: JPG, PNG, PDF
- Max size: 5 MB
- Min size: 1 KB
Equivalence Partitions:
Valid Partition:
EP-V1: JPG file, 2 MB
EP-V2: PNG file, 1 MB
EP-V3: PDF file, 3 MB
Invalid Partitions (File Type):
EP-I1: Disallowed type (e.g., .exe)
EP-I2: No extension
EP-I3: Wrong MIME type (renamed file)
Invalid Partitions (File Size):
EP-I4: Too small (< 1 KB)
EP-I5: Too large (> 5 MB)
EP-I6: Zero bytes (empty file)
Invalid Partitions (Other):
EP-I7: Null/no file selected
EP-I8: Corrupted file
Partitioning Strategies for Different Input Types
Numeric Inputs
Example: Grade (0-100)
Partitions:
- Invalid: < 0
- Valid: 0-100
- Invalid: > 100
String Inputs
Example: First Name
Partitions:
- Valid: Alphabetic (2-50 chars): "John"
- Invalid: Too short (< 2): "J"
- Invalid: Too long (> 50): "J" × 51
- Invalid: Contains numbers: "John123"
- Invalid: Contains special chars: "John@"
- Invalid: Empty: ""
Dropdown/Select Inputs
Example: Country Selection
Partitions:
- Valid: Any listed country: "USA"
- Invalid: Not in list: "Atlantis"
- Invalid: Empty/null selection
Boolean Inputs
Example: Newsletter Subscription Checkbox
Partitions:
- Valid: Checked (true)
- Valid: Unchecked (false)
Note: Only 2 partitions, both valid
Date Inputs
Example: Birth Date (must be 18+ years ago)
Partitions:
- Invalid: Future date
- Invalid: Recent date (< 18 years ago)
- Valid: Date ≥ 18 years ago
- Invalid: Invalid format: "13/45/2020"
- Invalid: Invalid date: "2020-02-30"
- Invalid: Empty
Multi-Dimensional Partitioning
When inputs have multiple criteria, create partitions for each dimension.
Example: Promotional Code Field
Dimensions:
- Format (alphanumeric, 6-10 chars)
- Validity (active vs expired)
- Usage limit (single-use vs multi-use, unused vs used up)
Partitions:
Dimension | Partition | Test Value |
---|---|---|
Format - Valid | 6-10 alphanumeric | “SAVE10” |
Format - Invalid | < 6 chars | “SAVE” |
Format - Invalid | > 10 chars | “SUPERSAVE123” |
Format - Invalid | Special chars | “SAVE@10” |
Validity - Valid | Active code | “ACTIVE2025” |
Validity - Invalid | Expired code | “EXPIRED2020” |
Usage - Valid | Unused code | “NEWCODE” |
Usage - Invalid | Fully used code | “USEDCODE” |
Common Mistakes
❌ Mistake 1: Overlapping Partitions
Bad:
- Partition 1: Age 0-50
- Partition 2: Age 50-100
Problem: Age 50 is in both partitions!
Good:
- Partition 1: Age 0-49
- Partition 2: Age 50-100
❌ Mistake 2: Missing Invalid Partitions
Bad (only testing valid):
- Partition: Valid age (18-65)
- Test: 30
Good (testing valid + invalid):
- Partition 1: Too young (< 18)
- Partition 2: Valid (18-65)
- Partition 3: Too old (> 65)
❌ Mistake 3: Testing Multiple Partitions at Once
Bad:
Test value: "john@"
(Tests both missing domain AND special char in username)
Good:
Test value 1: "john" (only missing domain)
Test value 2: "john@example" (only missing .TLD)
Test value 3: "jo hn@example.com" (only special char - space)
❌ Mistake 4: Choosing Unrepresentative Values
Bad:
Valid partition (age 18-65): Test value = 18
(This is a boundary, not mid-range)
Good:
Valid partition (age 18-65): Test value = 40
(Mid-range value, more representative)
Better (combining EP + BVA):
Valid partition (age 18-65): Test values = 18, 40, 65
EP Benefits
- Reduces Test Cases: From thousands to dozens
- Systematic Coverage: Ensures all input classes are tested
- Defect Detection: One test per partition catches entire partition failures
- Maintainable: Easy to update when requirements change
- Efficient: Maximizes coverage with minimum effort
EP Limitations
- Assumption-Based: Assumes all values in partition behave the same (may not always be true)
- Doesn’t Catch Boundary Errors: Combine with BVA for complete coverage
- Requires Understanding: Need to know requirements well to partition correctly
- May Miss Edge Cases: Some unique values might not fit partitions
Conclusion
Equivalence Partitioning is a foundational test design technique that:
- Divides input data into logical groups
- Tests one representative value per group
- Drastically reduces test cases while maintaining coverage
- Works best when combined with Boundary Value Analysis
Remember: If you test one value from a partition and it works, the entire partition should work. This principle allows you to test smarter, not harder.
Quick Reference: EP Process
1. Identify input conditions and requirements
2. Divide inputs into valid and invalid partitions
3. Ensure partitions don't overlap
4. Select one representative test value per partition
5. Combine with BVA for boundaries
6. Document partitions in test cases
7. Automate EP-based tests for regression
Further Reading
- ISTQB Foundation Syllabus: Test Design Techniques
- “Software Testing Techniques” by Boris Beizer (Chapter on Domain Testing)
- Companion article: “Boundary Value Analysis: Finding Bugs at the Edges”
- IEEE 829 Standard for Test Documentation