---
name: "functional_test_design"
description: "Design functional test cases from raw requirements by picking the right technique per requirement: equivalence partitioning and boundary value analysis for value constraints, decision tables for rule combinations, state transition diagrams for lifecycles, use case flows for actor journeys, pairwise reduction when combinations explode, and error guessing last. Use when turning a spec, user story or acceptance criteria into a test set, when a test set feels ad hoc, or when coverage needs a stated basis."
---

# Functional Test Design

## Purpose

Turn raw requirements into a test set where every case exists for a stated reason: a partition, a boundary, a rule, a transition, a flow, or a covered combination. The output names its coverage basis, so a reviewer can point at any requirement and ask what tests it, and point at any test and ask why it exists.

The method follows the black-box and experience-based techniques in the ISTQB Certified Tester Foundation Level syllabus (v4.0.1) and the combinatorial testing work in NIST SP 800-142. A test case here means the ISO/IEC/IEEE 29119 sense: preconditions, inputs, and expected results written before execution.

## Workflow

1. **Break the requirements into test conditions.** One condition per testable claim: a value rule, a business rule, a state rule, a flow step. Number each condition with the ID of the requirement it came from. A sentence with two claims becomes two conditions. Vague words such as fast, easy, or reasonable are not testable: flag them as open questions instead of testing around them.

2. **Route each condition to a technique.** Use `references/technique-selection-guide.md`. Value constraint goes to partitioning plus boundary analysis. Rule combination goes to a decision table. Lifecycle goes to a state model. Actor journey goes to use case flows. Many independent parameters go to pairwise, as a reduction step only. Error guessing runs last, over the finished set.

3. **Partition, then hit the boundaries.** For each input, list the partitions that should behave the same, valid and invalid, and take one value from each. For ordered partitions add boundary values: 2-value BVA as the floor (boundary plus the closest value in the next partition), 3-value BVA for money, limits with legal or safety weight, and any boundary that has failed before. Test invalid partitions one at a time so one rejection cannot mask another defect.

4. **Table the rule combinations.** List conditions and actions, write the full table, then collapse columns where a condition does not change the outcome and mark impossible columns infeasible with a reason. One test per feasible rule. A column you cannot fill from the requirement text is a spec question. Write it down as one. Do not fill it from what the code happens to do.

5. **Model lifecycles as states.** Draw the states, events, guards and actions. Build a state table so invalid transitions are visible. Cover in this order: all states, every valid transition (0-switch), pairs of consecutive valid transitions (1-switch) where sequence risk earns it, and invalid transitions attempted one per test with state and side effects checked after. A worked model with counted coverage is in `references/worked-order-lifecycle.md`.

6. **Walk the actor journeys.** One test for the main flow of each use case, one per alternate flow, one per exception flow. Each flow test asserts the end state and the hand-offs between steps, not just the final screen. A full checkout set that combines all six techniques is in `references/worked-checkout.md`.

7. **Reduce combination explosions with pairwise.** When factors are independent and the full cross product is too large, partition each factor first, add forbidden combinations, then generate a covering set with `bin/pairwise.py` and check it with its verify mode. Example, from a model file with factors and a forbidden list:

   `python3 bin/pairwise.py model.json --format csv`
   `python3 bin/pairwise.py model.json --verify-only rows.csv`

   The generator prints the exhaustive count and the generated count to stderr, and verify mode reports missing combinations and banned rows. Default strength is 2. Raise it to 3 only for factors that share money, state or code paths. If a named outcome depends on one exact full combination, that case stays in the decision table set even when pairwise covers every pair inside it.

8. **Add error guessing last, from sources.** Pull guesses from the defect history of the feature, support tickets and incident reports. Write the source next to each guessed case. Guesses with no source become exploratory charters, not committed cases.

9. **Assemble and self-audit.** Give every case an ID, its requirement and condition IDs, the technique that produced it, preconditions, inputs, steps and an expected result that can be checked true or false. Then audit both directions: every condition has at least one case, and every case traces to a condition or a named error guessing source. Cut orphans. Report the coverage achieved per technique, in its own terms, such as partitions covered, boundaries covered, rules covered, transitions covered, pairs covered.

## Output Contract

Deliver a test design set with all of the following:

1. **Condition list**: each test condition with its requirement ID and the technique routed to it.
2. **Case set**: for each case, ID, traced condition, technique, preconditions, concrete inputs, steps, and a checkable expected result including side effects (captures, refunds, messages, records) where the requirement names them.
3. **Models used**: the partition list, decision table with collapsed and infeasible columns marked, state diagram or state table, and flow list, whichever the techniques produced.
4. **Combination record**: for any pairwise set, the factor model, the forbidden combinations, the exhaustive count, the generated count, and the verify output showing zero missing combinations.
5. **Coverage statement**: per technique, what was covered out of what exists, plus open questions where the requirement was too vague or silent to test against.

## Operating Rules

1. **Expected results come from the requirement, never from the current behaviour of the code.** A test that asserts what the code already does can only catch change, not error.
2. **Never stack invalids.** One invalid partition, one invalid transition, one failing condition per test, unless the case exists to test how combined failures are handled and says so.
3. **Pairwise is a reduction step, not a routing shortcut.** It runs after partitioning and it never replaces decision table rules or state coverage.
4. **Count coverage in the unit of the technique.** Tests written is not a coverage number. Partitions, boundaries, rules, transitions and covered pairs are.
5. **Guards are part of the transition.** A transition with a guard needs one case where the guard holds and one where it fails, with the state after the failed guard checked.
6. **Infeasible is a finding, not a skip.** Record why a combination cannot occur. If nobody can explain why, treat it as feasible and test it.
7. **No invented numbers.** Limits, windows, percentages and thresholds in cases come from the requirement text or are marked as open questions. Example values in the references are teaching values only.
8. **Side effects are part of the expected result.** Where money, stock, messages or records move, the case states what must move, what must not move, and what must move exactly once.
