Skip to content
faker.tools
All 79

Guide · 11 min read

Boundary Value Analysis with Examples: A Practical Guide for Testers

Off-by-one errors live at the edges of every range. How to find the edges — including the ones nobody wrote down — and turn them into a small, sharp set of test cases.

Key takeaways

  • Boundary value analysis tests the edges of each valid range — the minimum, the maximum and their neighbours — because that is where off-by-one errors cluster.
  • Two-value BVA tests each boundary and its nearest invalid neighbour; three-value BVA adds the nearest valid neighbour. For a range of 1–100, that is 0, 1, 100, 101 versus 0, 1, 2, 99, 100, 101.
  • Use equivalence partitioning first to find the ranges, then BVA to pick the values. For several independent inputs, vary one at a time from a nominal value: 4n + 1 cases for n inputs.
  • The most dangerous boundaries are the hidden ones: integer type limits, VARCHAR byte limits, the end of a month, a daylight-saving gap, and the rounding of money.
  • Write each case down with its expected result. A boundary table with an accept-or-reject column is a test oracle you can automate directly.

Ask a developer to accept quantities from 1 to 100 and you will usually get correct code. Ask a hundred developers and some will write quantity < 100, some will validate on the client but not the server, and one will store the value in a column that cannot hold 100 with two decimals. None of those bugs shows up with a quantity of 50.

Boundary value analysis (BVA) is the discipline of testing where those bugs are: at the edges of each valid range. It is one of the oldest black-box techniques in testing, it is part of the ISTQB Foundation syllabus, and it is still one of the highest-yield ways to spend a small number of test cases.

Start with equivalence partitions

Before choosing values, find the ranges. Equivalence partitioning divides the input space into classes that the system should treat the same way. For an age field that accepts 18 to 65:

Partition Range Expected
Too young ≤ 17 Reject
Valid 18 – 65 Accept
Too old ≥ 66 Reject

Partitioning on its own would test one value per class — say 10, 40 and 80. That proves each class exists and says nothing about where the lines between them are drawn. Boundary value analysis picks the values at those lines.

Two-value and three-value BVA

Two-value BVA tests each boundary and its closest neighbour on the other side:

17, 18, 65, 66

Three-value BVA adds the closest neighbour on the same side:

17, 18, 19, 64, 65, 66

Three-value BVA catches an extra class of defect: a boundary implemented as a point rather than a range, such as age == 18 || (age > 19 && age <= 65) from a botched refactor. The cost is two extra cases per range, which is almost always worth paying.

Worked examples

A string length

A username must be 3 to 20 characters.

Case Value Expected
Empty "" Reject
min − 1 ab Reject
min abc Accept
min + 1 abcd Accept
max − 1 19 characters Accept
max 20 characters Accept
max + 1 21 characters Reject

Then ask the question that turns a good test into a great one: 20 of what? Twenty bytes, twenty UTF-16 code units, or twenty user-perceived characters? José is four characters and five UTF-8 bytes; a family emoji is one character, seven code points and twenty-five bytes. Repeat the max and max + 1 cases with emoji and accented characters, and the Unicode edge case generator will usually find a second boundary nobody specified.

Money

A transfer must be between 0.01 and 10,000.00.

Case Value Expected
Zero 0.00 Reject
min 0.01 Accept
max 10000.00 Accept
max + smallest unit 10000.01 Reject
Sub-cent 0.005 Reject, or round — decide which
Thousands separator 1,000.00 Decide which

The "smallest unit" for money is the currency's minor unit, not 1. And sub-unit values expose the real question: does the system reject, truncate or round? All three are defensible. Silently doing different things on the client and the server is not.

A date range

A booking must be at least one day and at most 365 days ahead.

The boundaries are not just "tomorrow" and "day 365". They are midnight in whose time zone? A user in Auckland and a server in UTC disagree about what "tomorrow" means for up to thirteen hours a day. Test the boundary at 23:59 and 00:01 local time, in a zone far from UTC, and across a daylight-saving change, where one local day is 23 or 25 hours long.

Several inputs at once

Testing every combination of boundaries across several inputs explodes quickly: six values for each of five inputs is 7,776 cases. The standard simplification is the single-fault assumption — most failures are caused by one input at a time — so you vary one input across its boundaries while holding the others at a nominal, safely valid value.

For each input, that gives min, min + 1, max − 1 and max, plus one shared case where everything is nominal: 4n + 1 test cases for n inputs. Adding the invalid values just outside each range (min − 1 and max + 1) gives 6n + 1. Five inputs need 21 or 31 cases, not thousands.

Where inputs interact — a discount that depends on both quantity and customer tier — add the boundary combinations for that pair specifically, rather than for everything.

The boundaries nobody wrote down

The specification gives you the explicit boundaries. The implementation adds its own, and these are the ones that reach production:

  • Integer types. A field validated as 1–100 in the UI may be parsed into a 32-bit integer on the server before validation runs. 2,147,483,648 overflows; 9,007,199,254,740,993 cannot be represented exactly in JavaScript.
  • Storage. VARCHAR(255) counts characters in some databases and bytes in others, and 255 four-byte emoji are 1,020 bytes.
  • Floating point. 0.1 + 0.2 is not 0.3. A total compared with === against a limit can fail at exactly the boundary.
  • Calendars. 29 February, 31 April (invalid), the last day of each month, the 2038 overflow of 32-bit Unix time, and the year 10,000 for four-digit year fields.
  • Pagination. The last page, a page with exactly one item, page 0, and a page beyond the end.
  • Rate limits and quotas. The 100th request in a window, and the 101st.
  • Client versus server. The UI blocks 101; does the API? Send boundary values to both.

The boundary value generator produces these hidden cases alongside the explicit ones — type limits, NaN, -0, leap seconds, empty arrays, null and missing keys — with an expected result for each.

Automate the table

A boundary table with an expected-result column is already a test. In pytest:

import pytest

CASES = [
    ("below minimum", 17, False),
    ("minimum", 18, True),
    ("minimum + 1", 19, True),
    ("maximum - 1", 64, True),
    ("maximum", 65, True),
    ("above maximum", 66, False),
]

@pytest.mark.parametrize("name,age,accepted", CASES, ids=[c[0] for c in CASES])
def test_age_validation(name, age, accepted):
    assert is_valid_age(age) is accepted

Named cases matter: a failure that says test_age_validation[maximum] tells you which edge broke without opening the code.

BVA pairs well with property-based testing. Libraries such as Hypothesis (Python) and fast-check (JavaScript) generate many inputs from a range and shrink failures to the smallest counter-example — which, more often than not, turns out to be a boundary.

When to agree before you test

BVA cannot rescue an ambiguous requirement. "Up to 100" might include 100 or not; "within 30 days" might mean 30 × 24 hours or 30 calendar dates. Settle inclusive versus exclusive, units, and time zones before writing the table. Most boundary "bugs" found late are really two people reading the same sentence differently — and the cheapest place to find that out is in the table, before the code exists.

Frequently asked questions

What is boundary value analysis in software testing?

A black-box test design technique that selects inputs at and around the edges of each valid range. Because developers commonly write < where they meant <=, or count from 1 where they meant 0, defects concentrate at boundaries, and a handful of well-chosen values finds them.

What is an example of boundary value analysis?

A field accepting ages 18 to 65. Three-value BVA tests 17, 18, 19, 64, 65 and 66. The values 17 and 66 should be rejected and the other four accepted; a defect at either edge shows up immediately.

How many test cases does boundary value analysis produce?

For one range, four values with two-value BVA and six with three-value BVA. For n independent inputs, the standard approach varies one input at a time while holding the others at a nominal value, giving 4n + 1 cases; adding the invalid values outside each range gives 6n + 1.

What is the difference between boundary value analysis and equivalence partitioning?

Equivalence partitioning divides inputs into classes that should behave the same and tests one value from each. Boundary value analysis tests the values at the edges between those classes. Partitioning tells you which ranges exist; BVA tells you which values in them to test.

Does boundary value analysis work for non-numeric inputs?

Yes. Any ordered or bounded property has boundaries: string length, number of items in a list, dates, file sizes, page numbers, rate limits. Even unordered inputs have edges — empty, one, and the maximum allowed count.

Tools this guide covers

Full directory →