---
name: test-case-design-techniques
source: https://app.decimal.ai/s/test-case-design-techniques@1/SKILL.md
source_sha256: fd96049808e0
---

# Test-case design techniques

Given an input space — a numeric range, a validated field, logic that combines several
conditions, or a set of independent parameters — derive the **smallest set of cases that still
covers what matters**. The failure mode this replaces is ad-hoc example picking: listing a few
"valid" inputs, one or two "invalid" ones, and stopping. That set feels reasonable and silently
misses the exact places bugs live — the far edge of a range, an untested invalid class, an
unexercised combination of conditions.

The move is to read the *shape* of the input space and apply the matching technique. This skill
decides **which cases to run**, not how to write the test code.

## Pick the technique from the shape of the input

| The input space is… | Use | What it buys you |
|---|---|---|
| An ordered range or size with limits (`1–100`, `8–64 chars`, a cutoff) | Boundary-value analysis | Catches off-by-one and wrong-comparison bugs that live *at* the edge |
| A field whose values fall into kinds (valid / several invalid kinds) | Equivalence partitioning | One case per kind; no wasted duplicates, no skipped invalid kind |
| An outcome driven by **several conditions combined** | Decision table | Every combination of conditions that changes the result is exercised |
| **Several independent parameters**, each with a few options | Pairwise / all-pairs | Most interaction bugs caught without the full cross-product blow-up |

These compose. A form field usually gets equivalence partitioning *plus* boundary-value analysis
on each numeric class. A settings screen gets pairwise across parameters, each parameter's values
first reduced by partitioning.

## Boundary-value analysis

For every boundary, bugs cluster at the transition, so test **three points per limit**: just
below, exactly on, just above. For a range `[min, max]` the six values are `min-1, min, min+1`
and `max-1, max, max+1`.

- Test **both ends.** The single most common gap is covering the lower limit carefully and never
  testing `max+1`. If there is more than one cutoff (e.g. score bands at 60/70/80), do this for
  **each** cutoff.
- Use the **adjacent** off-by-one value (`max+1`), not an arbitrary far value. For a limit of 65,
  the informative rejected case is 66, not 500 — 66 is what catches `>` written where `>=` was meant.
- Boundaries also live on **sizes and counts**: empty (0), one, the max length, one past the max;
  the first and last element; an empty collection vs a single-element one.

## Equivalence partitioning

Split the input into classes where any one member is a stand-in for the whole class, then test
**one representative from each class — valid and invalid**.

- Enumerate the **invalid** classes explicitly; they are what gets skipped. For a text field that
  is a positive integer, the invalid classes include: zero, negative, non-numeric, empty,
  whitespace-only, and (if bounded) out-of-range — not just "some bad string".
- Don't test two members of the same class — two different valid ages prove nothing extra. Spend
  the case on an untested class instead.
- Combine with boundary-value analysis: the *edges between* partitions are exactly the boundaries.

## Decision tables (combined conditions)

When the outcome depends on several conditions ANDed/ORed together, list the conditions as rows,
enumerate the combinations as columns (rules), and give the resulting action for each. Then keep
**one test per rule that produces a distinct outcome**.

- Cover the combination where each condition **independently flips the result**: hold everything
  else fixed and toggle one condition across its boundary so its individual effect is proven.
- Collapse **don't-care** combinations (when one condition already forces the outcome, the others
  are irrelevant — one case, not the full expansion).
- Include the combinations a happy-path list skips: all-conditions-false, and the mixed cases where
  the rule is *almost* satisfied. See `references/technique-catalog.md` for a worked truth table.

## Pairwise / all-pairs (independent parameters)

With N parameters (payment × shipping × currency × device …) the full cross-product explodes, but
most defects are triggered by an interaction of just **two** values. Pairwise builds a small set in
which **every pair of values from any two parameters appears together at least once**.

- Do **not** enumerate the full cross-product, and do **not** vary one parameter at a time holding
  the rest at a default — one-at-a-time never tests two non-default values together, so it misses
  interaction bugs by construction.
- The result is a handful of rows, not dozens: e.g. four 3-value parameters need ~9 pairwise cases,
  not 81. Construction procedure and a worked example are in `references/technique-catalog.md`.
- Pin any known bad interaction as an explicit extra case on top of the pairwise set.

## Output shape

Give the cases as a compact table or list: each case = the input value(s) or combination, which
class/boundary/rule it exercises, and the expected result (accept / reject / which outcome). State
the technique you applied and why the set is sufficient — i.e. what each case is *for*. Do not pad
the set with extra members of a class already covered.
