Quality is not optional. It's our standard. Free QA tools for testers and developers.

Boundary Value Test Data: How to Generate the Right Cases

QTQA3 Team

Boundary Value Test Data: How to Generate the Right Cases

Boundary value analysis (BVA) is one of the oldest, most reliable techniques in a tester’s toolbox. Yet many teams still treat it as a checkbox exercise—“add the min, max, and one‑off values” —and move on. The result is often a test suite that misses the subtle off‑by‑one bugs that surface in production. This post walks through a repeatable process for generating the right boundary test data, from requirement analysis to automated data creation, with concrete examples, tool guidance, and a checklist you can drop into your next sprint.


Why Boundary Values Matter

Defect classTypical symptomWhy BVA catches it
Off‑by‑one loopsLoop runs once too many / too fewThe loop condition is usually expressed as <= max or < max. The boundary is the exact point where the condition flips.
Integer overflow / underflowCrash or wrap‑around on extreme inputsThe max/min of the data type is a hard boundary; values just beyond it expose undefined behaviour.
Validation logic errors“Invalid” accepted or “Valid” rejectedBusiness rules (e.g., “age must be 18‑120”) are implemented as range checks. The edges of the range are where the logic changes.
UI limits (field length, dropdown size)Truncation, scroll‑bar appears, submit failsThe UI enforces a maximum length or item count; the boundary is the last allowed character / item.

If you only test “happy‑path” values (e.g., age = 30), you never exercise the decision points that cause the defects above. Boundary test data forces the system to evaluate those decision points.


Core Concepts

ConceptDefinitionTypical notation
Equivalence partitionA set of inputs that should be treated identically by the system.[min … max]
Boundary valueThe extreme values of an equivalence partition plus the immediate neighbours outside the partition.min‑1, min, min+1, max‑1, max, max+1
Inclusive vs. exclusive boundsWhether the limit itself is accepted () or rejected (<).age ≥ 18 (inclusive) vs. age > 18 (exclusive)
Multi‑dimensional boundariesWhen two or more fields interact (e.g., start‑date ≤ end‑date).Cross‑product of each field’s boundaries, filtered by the relational rule.
Data‑type limitsHard limits imposed by the programming language or database (e.g., INT32_MAX).2 147 483 647 for signed 32‑bit integer.

Understanding the inclusivity of each bound is the single biggest source of missed cases. A requirement that says “age must be between 18 and 120” is ambiguous until you confirm whether 18 and 120 are valid.


Decision Criteria for Selecting Boundaries

Not every field deserves a full six‑value set. Use the following criteria to decide the depth of coverage:

CriterionHigh coverage (6 values)Medium coverage (3 values)Low coverage (1 value)
Business criticalityCore revenue‑impacting fields (price, quantity)Supporting fields (discount code)Cosmetic fields (tooltip text)
Regulatory / complianceMandated ranges (age, dosage)Internal policy rangesOptional preferences
Historical defect densityFields with past off‑by‑one bugsFields with few defectsNew fields with no history
Data‑type riskNear language limits (INT_MAX, DATE_MAX)Well‑within type rangeFar from limits
Automation costLow (API, DB)Medium (UI)High (manual only)

Apply the table per field, then aggregate. A typical checkout flow might end up with high coverage for quantity, price, shipping‑zip; medium for discount‑code length; low for gift‑message.


Workflow for Generating Boundary Test Data

  1. Collect requirements – Pull acceptance criteria, API contracts, UI specs, and any regulatory docs.
  2. Identify equivalence partitions – For each input, write the valid range and any invalid partitions (e.g., negative numbers, non‑numeric strings).
  3. Determine inclusivity – Confirm with product owner or developer whether the limits are inclusive. Document the decision.
  4. Select coverage level – Use the decision‑criteria table.
  5. Produce the raw value list – Apply the six‑value pattern (or three‑value for medium) to each partition.
  6. Apply cross‑field constraints – Filter or expand the Cartesian product using relational rules (start ≤ end, password ≠ username).
  7. Encode as test data artefacts – CSV, JSON, SQL INSERT scripts, or a data‑factory method.
  8. Version‑control the data set – Store alongside test code; tag with the requirement version.
  9. Integrate into CI – Feed the data to automated tests (API, UI, contract) on every build.
  10. Review & prune – After each release, retire values that no longer map to a partition (e.g., a deprecated max‑length).

Worked Example: E‑commerce Checkout

1. Requirements snapshot

FieldValid rangeInclusivityBusiness criticality
quantity1 – 99inclusiveHigh
unit_price (cents)1 – 10 000inclusiveHigh
shipping_zip5‑digit US ZIPinclusive (exact 5 digits)High
discount_codealphanumeric, 4‑12 charsinclusiveMedium
gift_message0‑200 charsinclusiveLow

2. Equivalence partitions

FieldValid partitionInvalid partitions
quantity1 – 99≤0, ≥100, non‑numeric
unit_price1 – 10 000≤0, >10 000, non‑numeric
shipping_zip5‑digit numeric<5 digits, >5 digits, non‑numeric
discount_code4‑12 alnum<4, >12, special chars
gift_message0‑200 chars>200, non‑UTF‑8

3. Inclusivity confirmed

All limits are inclusive (e.g., quantity = 1 and = 99 are accepted).

4. Coverage level

FieldLevel
quantityHigh
unit_priceHigh
shipping_zipHigh
discount_codeMedium
gift_messageLow

5. Raw boundary values

FieldValues (high)Values (medium)Values (low)
quantity0, 1, 2, 98, 99, 100
unit_price0, 1, 2, 9 999, 10 000, 10 001
shipping_zip00000, 00001, 00002, 99998, 99999, 100000
discount_codeabc (3), abcd (4), abcdefghijkl (12), abcdefghijklm (13)
gift_message"" (empty), "a"*200, "a"*201

6. Cross‑field constraints

  • quantity * unit_price must not exceed INT32_MAX (2 147 483 647).
  • discount_code “FREE100” forces unit_price to 0 for the discounted line.

We generate the Cartesian product of the three high‑coverage fields (6 × 6 × 6 = 216 rows) then filter out any row where quantity * unit_price > 2 147 483 647. The remaining rows are paired with each discount‑code variant (4) and the three gift‑message variants (3), yielding a final data set of ≈ 2 600 rows—small enough for fast CI runs, large enough to hit every boundary.

7. Artefact example (JSON Lines)

{"quantity":1,"unit_price":1,"shipping_zip":"00001","discount_code":"abcd","gift_message":""}
{"quantity":1,"unit_price":1,"shipping_zip":"00001","discount_code":"abcd","gift_message":"a"*200}
{"quantity":99,"unit_price":10000,"shipping_zip":"99999","discount_code":"abcdefghijkl","gift_message":""}
...

The file lives at testdata/checkout_boundary.jsonl and is referenced by the API test suite:

import jsonlines, requests, pytest


@pytest.mark.parametrize("row", jsonlines.open("testdata/checkout_boundary.jsonl"))
def test_checkout_boundary(row):
    resp = requests.post("/api/checkout", json=row)
    assert resp.status_code in (200, 400)   # 400 only for known invalid combos

8. Version control & CI

  • Commit checkout_boundary.jsonl with the story ticket CHK‑1234.
  • In GitHub Actions, a job boundary-tests runs the parametrised test on every PR.
  • A nightly job regenerates the file from the source‑of‑truth spreadsheet (see Tool Considerations) and fails if the diff is non‑empty, forcing a review.

Tool Considerations

CategoryTypical toolsWhen to use
Spreadsheet‑drivenExcel / Google Sheets + custom scriptSmall number of fields, business analysts own the ranges.
Data‑factory librariesFactory Boy (Python), Faker.js (JS), Go‑fakeitUnit / integration tests where you need programmatic variation.
Domain‑specific generatorsQA3 free test data generator/tools/test-data-generatorQuick, zero‑setup generation of boundary sets for REST APIs, CSV, SQL, JSON.
Model‑based test toolsSpecFlow + SpecFlow+Excel, TOSCA, ModelJUnitWhen you have a formal model (state machine, BPMN) and want exhaustive cross‑field combos.
Database seedingFlyway / Liquibase + SQL scriptsEnd‑to‑end tests that require persisted data.

Practical tip: Keep the source of truth in a single place (e.g., a markdown table in the repo). All generators—whether a spreadsheet macro or the QA3 generator—read that source. This avoids drift between the documented requirements and the actual test data.

Using the QA3 Test Data Generator

  1. Paste the equivalence‑partition table (Markdown or CSV) into the UI.
  2. Choose “Boundary Value” mode.
  3. Select coverage level per column (high/medium/low).
  4. Export as JSONL, CSV, or SQL INSERT.

The generator respects inclusivity flags and can apply simple cross‑field filters (e.g., quantity * unit_price <= 2147483647). It does not replace a full model‑based engine, but for most micro‑service APIs it produces a ready‑to‑commit data set in seconds.


Validation Checks Before You Run

CheckHow to automateFailure action
Schema conformityJSON Schema / OpenAPI validator on each rowBlock CI, raise ticket
Range complianceSimple script asserting each value ∈ declared partitionBlock CI
Cross‑field rule satisfactionCustom predicate per rule (e.g., start <= end)Block CI
Duplicate detectionsort -u on primary key fieldsWarn, deduplicate
Size budgetCount rows; fail if > 10 000 (adjust per pipeline)Trim or split
DeterminismHash of generated file compared to committed hashFail if drift

Add these as a pre‑test stage in your pipeline. They cost milliseconds and catch generator regressions early.


Common Pitfalls

PitfallSymptomRemedy
Assuming inclusivityTests pass but production rejects valid max valueExplicitly document inclusivity; add a “boundary‑confirmation” checkbox in the requirement template.
Ignoring type limitsOverflow crashes only appear on 32‑bit buildsAdd INT_MAX, LONG_MAX, DATE_MAX as explicit invalid partitions.
Over‑generatingCI runtime > 30 min, flaky due to resource exhaustionApply the coverage‑level table; prune low‑risk fields to a single representative value.
Static data setsNew requirement adds a field; old data set no longer covers itRegenerate on every requirement change (automated via the generator).
Missing cross‑field constraintsInvalid combos (e.g., start‑date > end‑date) slip throughEncode constraints in the generator or filter step; unit‑test the filter itself.
Treating all fields equallyWasteful coverage on cosmetic fieldsUse the decision‑criteria table; revisit quarterly.
No versioningTeam unsure which data set matches which specStore data files in the same repo as the spec; tag with requirement IDs.

Checklist – Ready‑to‑Run Boundary Data

  • Requirements captured with explicit inclusive/exclusive bounds.
  • Equivalence partitions listed for every input (valid + invalid).
  • Coverage level assigned per field using the decision‑criteria table.
  • Boundary value list generated (six‑value for high, three‑value for medium, one‑value for low).
  • Cross‑field constraints expressed as code or generator filters.
  • Data artefact produced in the format consumed by the test harness.
  • Schema, range, constraint, duplicate, size, and determinism checks pass in CI.
  • Artefact committed alongside the requirement version (e.g., CHK‑1234).
  • CI job runs the parametrised boundary tests on every PR.
  • Nightly regeneration job compares generated output to committed file; fails on drift.

Next Steps

  1. Pick a single high‑risk feature (e.g., payment amount validation) and run the workflow end‑to‑end this sprint.
  2. Commit the generated boundary file and add the pre‑test validation stage to your pipeline.
  3. Measure the defect escape rate for off‑by‑one bugs over the next two releases; adjust coverage levels based on the data.
  4. Automate regeneration by wiring the QA3 free test data generator at /tools/test-data-generator into your nightly job, so the data set always reflects the current with the latest requirements.

Start small, prove the cycle, then expand to the rest of the API surface. The payoff is a test suite that actually exercises the decision points where bugs live—without exploding your test‑run time.

Read more

How to Measure Test Data Quality Before a Test Run

A step-by-step guide for “How to Measure Test Data Quality Before a Test Run,” covering prerequisites, implementation choices, validation, and common failure modes.

Test Data Generation Mistakes That Make Tests Flaky

A practical risk review of “Test Data Generation Mistakes That Make Tests Flaky,” with warning signs, safeguards, and fixes for real QA workflows.

Test Data Factories vs Fixtures vs Seed Scripts

A buyer-focused guide to “Test Data Factories vs Fixtures vs Seed Scripts,” with concrete selection criteria, trade-offs, and an evaluation path QA teams can use.