What Is Boundary Value Analysis?
Boundary Value Analysis (BVA) is a black-box test design technique that focuses on testing values at the edges of equivalence classes. While Equivalence Partitioning tells you which groups to test, BVA tells you where within those groups defects are most likely to hide.
Why Boundaries Matter
Studies consistently show that a disproportionate number of software defects occur at boundary values. The reason is simple: developers write conditions like if (age >= 18) or if (quantity <= 100), and off-by-one errors (> vs >=, < vs <=) are among the most common coding mistakes.
# Intended: accept ages 18 and above
if age > 18: # Bug! Rejects 18-year-olds
allow_access()
# Correct:
if age >= 18:
allow_access()
This single-character difference (> vs >=) causes a defect that only manifests at the boundary value 18. Testing with age=25 would never catch it.
Two-Value BVA (Standard)
The most common approach tests two values per boundary: the boundary itself and the value just outside it.
For a valid range of 1 to 100:
| Boundary | Test Values | Expected |
|---|---|---|
| Lower | 0 (just below) | Invalid |
| Lower | 1 (boundary) | Valid |
| Upper | 100 (boundary) | Valid |
| Upper | 101 (just above) | Invalid |
4 test cases cover both boundaries.
Three-Value BVA (Rigorous)
For higher-risk systems, test three values per boundary: just below, the boundary, and just above.
For the same range 1 to 100:
| Boundary | Test Values | Expected |
|---|---|---|
| Lower | 0 | Invalid |
| Lower | 1 | Valid |
| Lower | 2 | Valid |
| Upper | 99 | Valid |
| Upper | 100 | Valid |
| Upper | 101 | Invalid |
6 test cases for more thorough coverage. The additional values (2 and 99) catch defects where the boundary condition is off by more than one.
BVA for Different Data Types
Integers: Straightforward — boundary ± 1.
- Range 1-10: test 0, 1, 10, 11
Floating-point: Use the smallest meaningful increment.
- Range 0.0-1.0: test -0.01, 0.0, 1.0, 1.01 (or -0.001, 0.0, 1.0, 1.001 depending on precision)
Strings (length): Test at length boundaries.
- Username 3-20 chars: test 2 chars, 3 chars, 20 chars, 21 chars
Dates: Test at date boundaries.
- Valid dates Jan 1 - Dec 31: test Dec 31 of previous year, Jan 1, Dec 31, Jan 1 of next year
Collections (size): Test at count boundaries.
- Cart items 1-50: test 0 items, 1 item, 50 items, 51 items
Combining EP and BVA
These two techniques are natural partners. EP identifies the partitions; BVA focuses on their edges.
Example: Shipping cost by weight
| Weight (kg) | Shipping |
|---|---|
| 0.1 – 1.0 | $5 |
| 1.01 – 5.0 | $10 |
| 5.01 – 20.0 | $20 |
| > 20.0 | $35 |
EP representatives (middle of each class): 0.5, 3.0, 12.0, 25.0
BVA values (at boundaries): 0.09, 0.1, 1.0, 1.01, 5.0, 5.01, 20.0, 20.01
Combined, you get thorough coverage with just 12 test cases.
Advanced BVA Techniques
Multi-Dimensional Boundaries
When a system has multiple interacting parameters, boundaries exist in multiple dimensions. Consider a pricing engine:
- Quantity: 1-1000
- Unit price: $0.01-$9999.99
The interesting boundaries aren’t just the individual parameter edges but the combinations:
| Test | Quantity | Unit Price | Total | Why |
|---|---|---|---|---|
| 1 | 1 | $0.01 | $0.01 | Minimum possible total |
| 2 | 1000 | $9999.99 | $9,999,990 | Maximum possible total |
| 3 | 1 | $9999.99 | $9999.99 | Max price, min quantity |
| 4 | 1000 | $0.01 | $10.00 | Min price, max quantity |
Internal Boundaries
Not all boundaries are at the edges of valid/invalid ranges. Internal boundaries separate different processing paths within the valid range:
def calculate_tax(income):
if income <= 10000:
return income * 0.10
elif income <= 50000:
return 1000 + (income - 10000) * 0.20
else:
return 9000 + (income - 50000) * 0.30
Internal boundaries at $10,000 and $50,000 are just as important as the overall valid range boundaries. Test: $9,999, $10,000, $10,001, $49,999, $50,000, $50,001.
Implicit Boundaries
Some boundaries aren’t in the specification but exist in the implementation:
- Integer overflow: 2,147,483,647 (max 32-bit signed int)
- Array limits: 0-indexed vs 1-indexed collections
- Empty/null: The boundary between “something” and “nothing”
- Precision limits: Floating-point comparison issues at very small differences
BVA for Time-Based Systems
Time boundaries are particularly tricky:
| Scenario | Boundary Values |
|---|---|
| Session timeout (30 min) | 29:59, 30:00, 30:01 |
| Daily batch job (midnight) | 23:59:59, 00:00:00, 00:00:01 |
| Leap year (Feb 28/29) | Feb 28, Feb 29, Mar 1 |
| DST transition | 01:59, 02:00 (spring forward), 01:00 (fall back) |
Exercise: BVA for a Password Policy
Scenario: A system enforces this password policy:
- Length: 8-64 characters
- Must contain at least 1 uppercase letter
- Must contain at least 1 digit
- Must contain at least 1 special character (!@#$%^&*)
Task: Identify all boundary values for the length requirement and design test cases using three-value BVA. Then identify how the character-type requirements create additional boundaries.
Hint
For length, the boundaries are 8 (minimum) and 64 (maximum). Three-value BVA means testing 7, 8, 9 at the lower boundary and 63, 64, 65 at the upper.
For character types, the boundary is between 0 and 1 occurrence of each required type. Think about a password with exactly 0 uppercase letters vs exactly 1.
Solution
Length BVA (three-value):
| # | Length | Test Value | Expected |
|---|---|---|---|
| 1 | 7 chars | Aa1!xyz | Reject (too short) |
| 2 | 8 chars | Aa1!xyzw | Accept (lower boundary) |
| 3 | 9 chars | Aa1!xyzwq | Accept (just above lower) |
| 4 | 63 chars | Aa1! + 59 x’s | Accept (just below upper) |
| 5 | 64 chars | Aa1! + 60 x’s | Accept (upper boundary) |
| 6 | 65 chars | Aa1! + 61 x’s | Reject (too long) |
Character-type boundaries:
| # | Test | Expected |
|---|---|---|
| 7 | 0 uppercase: aa1!xyzw | Reject |
| 8 | 1 uppercase: Aa1!xyzw | Accept |
| 9 | 0 digits: Aa!!xyzw | Reject |
| 10 | 1 digit: Aa1!xyzw | Accept |
| 11 | 0 special: Aa1bxyzw | Reject |
| 12 | 1 special: Aa1!xyzw | Accept |
Total: 12 test cases covering all boundaries for length and character-type requirements.
Pro Tips
- Always pair BVA with EP. BVA without EP misses the big picture; EP without BVA misses the most defect-dense areas.
- Don’t forget zero and empty. Zero items, zero-length strings, and empty collections are boundaries that reveal null pointer exceptions, division by zero, and other common defects.
- Consider the minimum increment. For integers it’s 1, for currency it might be 0.01, for timestamps it could be 1 second or 1 millisecond. Use the right increment for your domain.
- Test both sides of every boundary. A boundary has two sides — valid and invalid. Missing either side leaves defects uncovered.
- In APIs, test boundary values in request parameters, headers, and response handling. Boundaries in API pagination (page=0, page=1, page=maxInt) frequently contain bugs.