The bugs that reach production live at the edges: the 256th character, February 29th, the emoji in the surname, the two flags nobody toggled together. AI amplifies three classic techniques — boundary analysis, pairwise combination, property-based testing — into an edge-case engine, while coverage judgment stays yours.
Tier: Foundation · Workflow area: Test design & planning
This playbook uses an LLM as a tireless edge-case ideation partner, then hardens the best ideas with real tooling: PICT for pairwise reduction and Hypothesis for property-based tests that find the inputs no example list contains. The model proposes; the techniques and tools verify.
When NOT to use:
brew install pict or the Microsoft binary) for pairwise reduction.pip install hypothesis) — or fast-check/jqwik for your stack.Extract the input space first. For the feature, list every field with its type, limits, and format; every flag or mode; every locale/timezone assumption. This grounding stops the model brainstorming about a product it imagined.
Run the structured ideation prompt — categories, not free association:
Feature inputs: [paste field spec: name (1-100 chars, UTF-8), amount
(0.01-99999.99, EUR/INR), date (booking, future only), role (admin/member),
plan (free/pro)].
Generate edge and negative cases in EXACTLY these categories:
1. BOUNDARIES — at, one-below, one-above every numeric/length limit.
2. FORMAT & ENCODING — Unicode (CJK, RTL, emoji, zero-width, combining
diacritics), whitespace, null bytes, oversized input.
3. TIME — leap day, DST transitions both directions, year boundary,
epoch-adjacent, timezone-vs-UTC date disagreement.
4. INJECTION-ADJACENT — quotes, angle brackets, template braces, path
traversal strings (for handling, not exploitation).
5. STATE & CONCURRENCY — double-submit, stale read, mid-flow permission
revocation.
For each: input value, expected safe behavior, risk (H/M/L) with one-line
justification.
Good output is specific enough to execute directly:
BOUNDARY: amount = 99999.99 → accepted; amount = 100000.00 → rejected
with "exceeds maximum", no partial charge created. Risk H: money.
TIME: booking 2028-02-29 from UTC+13 on 2028-02-28 23:30 local — date
valid at client, must not double-shift to Mar 1. Risk M.
Compress combinations with pairwise. For the flag/role/plan/locale matrix, don't test the cross-product — generate a PICT model and let it cut the set while covering every pair:
role: admin, member
plan: free, pro, enterprise
locale: en-US, de-DE, ja-JP, ar-SA
payment: card, invoice, none
pict model.txt > pairwise-cases.txt && wc -l pairwise-cases.txt
72 combinations become ~16 rows. Ask the model which specific pairs deserve forcing as explicit constraints (e.g., IF [plan] = "free" THEN [payment] = "none";) — then verify those constraints against the real product rules, because an invented constraint silently deletes coverage.
Turn the strongest categories into properties. Example lists age; properties keep generating. Have the assistant draft Hypothesis tests for the invariants behind the categories:
from hypothesis import given, strategies as st
@given(st.text(min_size=1, max_size=100))
def test_name_roundtrips_through_create_and_fetch(name):
created = api.create_customer(name=name)
assert api.get_customer(created.id).name == name
@given(st.decimals(min_value="0.01", max_value="99999.99", places=2))
def test_amount_never_loses_precision(amount):
assert api.quote(amount).total == amount
pytest test_properties.py --hypothesis-show-statistics
Review generated strategies hard: models routinely write strategies that quietly exclude the interesting inputs (ASCII-only text, positive-only integers) — widen them and let shrinking find the minimal failing case.
Triage into the suite. Rank by risk, promote the High items into permanent regression cases (pb-01's import path), keep properties in the unit/integration tier, and record the pruned ideas in the test plan as considered-and-rejected — that list is gold at the next escape review.
For encoding categories, seed the prompt with real corpora — the big-list-of-naughty-strings repository and your own past escape bugs — the model recalls famous edge cases well but your product's specific history better.
fc.assert(fc.property(...)); pair it with the Playwright suite so API-level properties and UI example tests share the boundary spec.Keep a living "edge-case museum" file of every boundary bug that ever escaped to production, and paste it into each ideation prompt — the model generalizes from your escapes into neighboring cases, which is exactly where the next one is hiding.