Skip to content

Software Quality

Combinatorial and pairwise testing

Combinatorial testing checks every combination of any two parameter values with a small set of test rows, rather than the full product of every option. That set is a covering array, and its general form guarantees that every combination of any t parameters appears at least once: at interaction strength t = 2pairwise testing — every pair of parameter values is covered, at t = 3 every triple. The array replaces full enumeration: a system with ten on/off options has 210=10242^{10} = 1024 configurations, twenty options a million. Testing every combination is infeasible; testing none of the interactions misses the bug that appears only when option A and option B are both set.

The covering array is generated, not enumerated by hand, and the tester still supplies an oracle for each row.

What it catches

  • Interaction faults. Defects that surface only under a specific combination of parameter values — a codec that fails only when hardware decode is on and the input is interlaced, a form that rejects valid data only for one locale–currency pair.
  • Configuration and compatibility bugs. Browser × OS × resolution matrices, feature-flag combinations, driver/firmware pairings.
  • Holes a hand-written matrix missed. A covering array is complete to strength t by construction; an author's ad-hoc selection has no such guarantee and usually skips the unglamorous pairs.

What it does not catch: faults that need more than t interacting parameters (raise t, at rising cost); faults tied to a specific value outside the set the tester modeled (combinatorial testing covers the modeled values, not the whole domain — pair it with boundary-value choices); and, as with any input-selection method, anything its oracle doesn't check. A parameter the model omits is invisible to the array.

The interaction rule

Across fault studies in several domains — medical devices, a browser, an HTTP server, a NASA database — (Kuhn et al. 2004)1 found that failures were triggered by combinations of relatively few parameters: most by one or two, and essentially all by six or fewer.

The implication is the method's whole case: if every fault is triggered by at most t parameters, then a t-way covering array is effectively exhaustive — it provokes every fault a full Cartesian sweep would, at a tiny fraction of the cost. Pairwise (t = 2) already covers the bulk; t = 4 to 6 approaches the empirical ceiling. This is an observed regularity, not a theorem: a system whose faults need a seven-way interaction will not be covered by a six-way array.

The t-way subsumption ladder

Interaction strength is a containment order. A t-way covering array also covers every (t1)(t-1)-way combination, so

each-choice (1-way)pairwise (2-way)3-wayexhaustive \text{each-choice (1-way)} \sqsubset \text{pairwise (2-way)} \sqsubset \text{3-way} \sqsubset \cdots \sqsubset \text{exhaustive}

The ladder is bounded above by the full Cartesian product — the same exhaustive-within-a-bound shape the guarantee axis gives to bounded-exhaustive enumeration and bounded model checking. Raising t climbs the ladder toward a stronger guarantee at a higher row count; the array size grows roughly logarithmically in the number of parameters but sharply in t, which is why most practice stops at two or three.

Tools

  • ACTS (NIST) — the reference generator; supports constraints (forbidden combinations), variable-strength arrays, and combinatorial coverage measurement of an existing suite. Free, Java, with a GUI and CLI.
  • PICT (Microsoft) — open-source command-line generator; compact model syntax, constraints, seeding, and sub-model weighting.
  • CAgen — a fast covering-array generator for high strengths and large models, available as a web tool and CLI.
  • Language libraries: allpairspy (Python), jqwik's @Combinatorial and table-based generation (Java), tcases (model-based, JSON/YAML). Most build pairwise arrays inline so the combinations live next to the test.

The practical differentiator is constraint support: real configuration spaces forbid combinations (a feature that exists only on one platform), and a generator that can exclude them avoids wasting rows on impossible inputs.

When to use, when not

Use:

  • Large configuration, compatibility, or feature-flag spaces where the full product is intractable but interactions matter.
  • API and function parameters with several discrete options each.
  • Software product lines and variability models, where ACTS-style constrained generation is the standard tool.

Don't:

  • When parameters genuinely don't interact — independent options are better covered one at a time.
  • When the fault is value-specific rather than combination-specific — an off-by-one at a numeric boundary is a job for boundary-value example tests, not a covering array over coarse value classes.
  • As a source of oracles. Combinatorial testing chooses inputs; pair it with a real oracle (expected outputs, invariants, or at least a crash check).

Combinatorial coverage and fault localization

Two adjacent uses fall out of the same model. Combinatorial coverage measurement runs the covering-array construction in reverse: given an existing suite, measure what fraction of all t-way combinations it already exercises — a gap-finder for hand-written suites, reported by ACTS. Fault localization exploits the array's structure after a failure: because each combination's coverage is known, the rows that pass and fail narrow down which parameter interaction is responsible.

Evidence

  • The interaction-rule studies. Across multiple application domains, observed failures were triggered by few interacting parameters, with none exceeding six (Kuhn et al. 2004)1. The sample is specific, if varied, so the result is a regularity rather than a guarantee that holds for every system.

Further reading

  • The NIST practitioner guidance (Kuhn et al. 2010)2 collects the generation algorithms, constraint handling, coverage measurement, and worked case studies; it is the standard reference for applying the method and the source of the ACTS tooling.
  • Combinatorial testing's place among automated test-generation methodologies is cataloged in the standard survey (Anand et al. 2013)3.

Exhaustive enumeration of a bounded input space, compressed several ways

These methods all exhaustively enumerate a bounded input space; each compresses the exponential blow-up a different way. Decision tables compress losslessly: a "don't-care" entry folds together combinations that share an outcome, so every case still maps to one rule, and the row carries its own expected decision. Combinatorial and pairwise testing sample: a covering array keeps every t-way interaction and drops the higher-order ones, with the oracle supplied separately. Bounded-exhaustive property testing bounds the size: it enumerates every value up to a depth/size k (SmallCheck), relying on the small-scope hypothesis that faults surface small. Exhaustive coverage runs from MCC (the full 2ⁿ truth table) down to MC/DC's n+1 independence criterion, and audits an existing suite rather than generating one. Type systems reach the same completeness by construction: an exhaustiveness check on a match / switch over a sum type has the compiler verify every variant is handled, with no case written by hand. State-, path-, and schedule-space enumerators such as model checking, symbolic execution, and systematic concurrency testing share the idea over a different space; see the reachability clusters.

Generative testing

These methods blur together because they all run the code and check the result without a hand-written expected value. Two roles pull them apart, and a single test picks one of each.

Input generators choose the input, and differ by the steering signal (the Generative subtree of the input axis):

  • Random: property-based testing samples from a generator or schema.
  • Coverage feedback: fuzzing mutates inputs steered by coverage (raw bytes); automated test generation runs the same feedback loop as a fitness-guided search over structured call sequences.
  • Solver: symbolic execution derives an input that reaches a chosen path.
  • Systematic: combinatorial testing builds a covering array over every t-way combination.

Oracle suppliers provide the verdict when no expected value is written:

Pick one generator and one oracle: they compose. A coverage-guided fuzzer that checks a metamorphic relation is fuzzing and metamorphic at once. (The written-answer end, where the author picks rows and answers by hand, is example / parameterized tests.)

Classification

  • Quality dimensions: Functionality, Reliability, Security (security-configuration interaction faults).
  • Area: Configuration spaces, feature-flag and compatibility matrices, API parameter combinations, product-line variability — anywhere interaction faults hide in combinations no one enumerated by hand.
  • Guarantee: Exhaustive within the interaction strength t — every t-way combination is covered; higher-order interactions are not.

Referenced by

References


  1. Kuhn, D. Richard, Dolores R. Wallace, and Albert M. Gallo. 2004. "Software Fault Interactions and Implications for Software Testing." IEEE Transactions on Software Engineering 30 (6): 418–21. https://doi.org/10.1109/TSE.2004.24

  2. Kuhn, D. Richard, Raghu N. Kacker, and Yu Lei. 2010. Practical Combinatorial Testing. NIST Special Publication 800-142. National Institute of Standards; Technology. https://doi.org/10.6028/NIST.SP.800-142

  3. Anand, Saswat, Edmund K. Burke, Tsong Yueh Chen, et al. 2013. "An Orchestrated Survey of Methodologies for Automated Software Test Case Generation." Journal of Systems and Software 86 (8): 1978–2001. https://doi.org/10.1016/j.jss.2013.02.061