The Basics and Applications of Efficient Test Design with Equivalence Partitioning and Boundary Value Analysis
Equivalence partitioning and boundary value analysis exist to cut the number of test cases without losing the places where defects actually appear. They are used as a pair.
What matters more in practice is using them during specification review rather than during test execution. Writing out the boundaries reliably surfaces values whose behaviour the specification never defines. Removing those before implementation changes how much rework you face later.
This article covers both techniques and the boundaries teams most often miss—character counts, dates, and full-width digits.
Equivalence partitioning
Group inputs that should produce the same result, then pick one representative from each group.
For an age field accepting 0 to 120:
| Class | Range | Representative |
|---|---|---|
| Valid | 0–120 | 30 |
| Invalid (below) | −1 and lower | −5 |
| Invalid (above) | 121 and higher | 130 |
The justification is that any value within a class exercises the same code path. If 30 passes, 31 and 32 will too.
When that assumption breaks, split the class again. Add a rule such as “under 20 cannot purchase” and the valid class must divide into 0–19 and 20–120.
Boundary value analysis
Target the edges of each class. Defects cluster at boundaries, not in the middle of ranges—< written where <= belonged, or i < n confused with i <= n.
-1 | 0 ... 120 | 121
↑ ↑
lower bound upper bound
Two-value or three-value?
There are two conventions. Agree on one as a team, or case counts vary by author.
| Values taken | For 0–120 | Cases | |
|---|---|---|---|
| Two-value | The boundary and its outside neighbour | −1, 0, 120, 121 | 4 |
| Three-value | The boundary and both neighbours | −1, 0, 1, 119, 120, 121 | 6 |
Two-value is normally sufficient. Reserve three-value for boundaries with unusually complex logic, or where a mistake is very costly—payment limits, for instance.
How the two fit together
Partitioning decides where to look; boundary analysis decides which values to enter. Never the other way round.
- Equivalence partitioning: divides the input space → identifies what needs testing
- Boundary value analysis: locates each class edge → fixes the actual values
Together, one representative plus the boundaries covers the whole range. You never need all 121 values.
Example 1: an age field
Specification: age accepted between 0 and 120.
| Value | Expected | Technique |
|---|---|---|
| −1 | Error | Boundary |
| 0 | OK | Boundary |
| 30 | OK | Partitioning (representative) |
| 120 | OK | Boundary |
| 121 | Error | Boundary |
Do not stop here. Numeric boundaries alone leave these gaps in production:
| Input | Is it in the specification? |
|---|---|
| Empty | Is the field required or optional? |
| Full-width digits “30” | Accept, convert, or reject? |
| Decimal “30.5” | Integers only? |
| Surrounding spaces ” 30 “ | Trimmed? |
| Text “abc” | What is the error message? |
Show this table to a developer and several rows will turn out to be undecided. Resolving them before implementation is the real value of the technique.
Example 2: a coupon threshold
Specification: apply the coupon when the order total is 1,000 yen or more.
| Value | Expected |
|---|---|
| 999 | Not applied |
| 1,000 | Applied |
| 1,001 | Applied |
“1,000 or more” includes exactly 1,000. “More than 1,000” does not. That distinction becomes >= versus > in the implementation.
For money, there is more hiding in that one sentence:
- Is the comparison before or after tax? Very often unstated
- Does it include shipping?
- Is it the total before or after other discounts?
Example 3: password length
Specification: passwords are 8 to 16 characters.
| Length | Expected |
|---|---|
| 7 | Error |
| 8 | OK |
| 16 | OK |
| 17 | Error |
With character counts, the question becomes what counts as one character. This is the most frequently missed area in practice.
- Emoji: JavaScript’s
lengthcounts many emoji as two (surrogate pairs) - Combining characters: a base letter plus a diacritic counts as two
- Newlines:
\r\nis two characters, and which is sent varies by OS - Non-ASCII: with a byte-based limit, a CJK character is three bytes in UTF-8
When the front end and the server define “16 characters” differently, input passes validation and then fails on save. Always include one test case containing an emoji.
Dates need special attention
Dates have boundaries in places numbers do not. Checking only the ends of the range misses them.
| Boundary | Example |
|---|---|
| End of month | 31 January, 30 April |
| Leap day | 2024-02-29 (2100 is not a leap year) |
| Year rollover | 31 December → 1 January |
| Midnight | 23:59:59 → 00:00:00 |
| Time zones | Hours where UTC and local date differ |
A relative rule such as “30 days from today” produces different results depending on when you run it. Write those tests with a fixed clock.
Change one invalid value at a time
This is about how you write cases, not the technique itself. Never combine several invalid values.
Bad: age = -1, name empty, email malformed, all in one case
→ it errors, but you cannot tell which field caught it
Good: keep every other field valid and set only age to -1
→ confirms the age validation is actually running
Combine invalid values and the first failure short-circuits the rest, so you cannot tell whether the other checks exist. A missing age validation goes unnoticed.
Valid values, by contrast, can be combined freely—one all-valid case is the norm.
Partition the output too
Looking only at inputs hides output branches.
If shipping is “0, 300 or 550 yen depending on the order total”, there are three output classes. Beyond the input boundaries, confirm all three shipping values actually occur.
Where several conditions interact, decision table testing organises the combinations more reliably.
Cautions
Do not create too many cases
The point of partitioning is reduction. Taking two or three values from the same class means either the classes are wrong or you have lost sight of the goal.
Get the boundaries right
For “display up to 100 items”, the boundaries are 100 and 101—and also 0 and 1. The empty-list state is one of the most commonly unimplemented cases.
Know what these techniques do not cover
| Situation | Technique |
|---|---|
| Business logic with interacting conditions | Decision table testing |
| Screens and states that transition | State transition testing |
| Too many configuration combinations | Pairwise testing |
These two techniques address the range of a single input. Relationships between fields belong elsewhere.
Summary
- Partitioning decides where to look; boundary analysis decides which values
- Two-value boundaries are usually enough. Agree as a team whether to use three
- Do not stop at numeric boundaries. Check empty, full-width digits, decimals and surrounding spaces
- “1,000 or more” includes 1,000. Whether it is pre- or post-tax is usually unstated
- Character counts change with emoji and combining characters. Include one such case
- Date boundaries: month end, leap day, year rollover, time zones
- One invalid value per case. Valid values may be combined
- Partition outputs as well as inputs
- Zero-item and empty-list states are boundaries, and frequently unimplemented
The real payoff is finding gaps in the specification, not executing tests. Using these upstream is covered in QA in waterfall development.