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 TypeRangeDescription
Invalid< 1Too few tickets
Valid1-10Acceptable range
Invalid> 10Too many tickets

Step 3: Select Test Values

Choose one representative value from each partition:

PartitionTest ValueExpected Result
Invalid (< 1)0Error: “Minimum 1 ticket required”
Valid (1-10)5Booking successful
Invalid (> 10)15Error: “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:

PartitionRangeEP Test ValueEP+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:

PartitionWeight RangeTest ValueExpected Cost
EP-I1≤ 00Error: “Invalid weight”
EP-I2< 0-5Error: “Invalid weight”
EP-V10.01 - 53$5
EP-V25.01 - 2012$10
EP-V320.01 - 5030$25
EP-V4> 5075“Contact for quote”
EP-I3Non-numeric“abc”Error: “Weight must be numeric”
EP-I4NullnullError: “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: ""
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:

  1. Format (alphanumeric, 6-10 chars)
  2. Validity (active vs expired)
  3. Usage limit (single-use vs multi-use, unused vs used up)

Partitions:

DimensionPartitionTest Value
Format - Valid6-10 alphanumeric“SAVE10”
Format - Invalid< 6 chars“SAVE”
Format - Invalid> 10 chars“SUPERSAVE123”
Format - InvalidSpecial chars“SAVE@10”
Validity - ValidActive code“ACTIVE2025”
Validity - InvalidExpired code“EXPIRED2020”
Usage - ValidUnused code“NEWCODE”
Usage - InvalidFully 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

  1. Reduces Test Cases: From thousands to dozens
  2. Systematic Coverage: Ensures all input classes are tested
  3. Defect Detection: One test per partition catches entire partition failures
  4. Maintainable: Easy to update when requirements change
  5. Efficient: Maximizes coverage with minimum effort

EP Limitations

  1. Assumption-Based: Assumes all values in partition behave the same (may not always be true)
  2. Doesn’t Catch Boundary Errors: Combine with BVA for complete coverage
  3. Requires Understanding: Need to know requirements well to partition correctly
  4. 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