npx claudepluginhub nwave-ai/nwave --plugin nwThis skill uses the workspace's default tool permissions.
Core property-based testing knowledge, language-agnostic. Load language-specific skills for framework syntax.
Guides property-based testing for serialization roundtrips, idempotence, invariants, parsing, validation, and smart contracts across languages.
Guides property-based testing for serialization, validation, normalization, and pure functions with property catalog, pattern detection, and library references.
Generative testing with QuickCheck/Hypothesis; testing properties that should hold for all inputs.
Share bugs, ideas, or general feedback.
Core property-based testing knowledge, language-agnostic. Load language-specific skills for framework syntax.
Instead of specifying individual test cases, define properties (rules for all valid inputs) and let the framework generate hundreds of random inputs to attempt falsification. On failure, framework automatically shrinks input to minimal reproducing case.
PBT complements example-based tests. Use both.
| Property Type | Use When | Example Domain |
|---|---|---|
| Invariant | Output has structural guarantees regardless of input | sort preserves length, tree stays balanced |
| Idempotency | Applying operation twice equals applying once | formatting, deduplication, HTTP PUT |
| Round-trip | Two operations are inverses | encode/decode, serialize/deserialize, compress/decompress |
| Oracle | Simpler reference implementation exists | optimized algo vs naive, new parser vs stdlib |
| Metamorphic | Can't verify single output but can relate multiple outputs | ML classifiers, search engines, numeric computation |
| Commutativity | Operation order shouldn't matter | set operations, independent migrations |
| Inductive | Property holds for base case and each incremental step | recursive data structures, incremental builds |
| Hard-to-find, easy-to-verify | Solution expensive to compute but cheap to check | pathfinding, factorization, compilation |
Start: What does the function do?
|
+-- Has an inverse? (encode/decode, serialize/deserialize)
| -> Round-trip: decode(encode(x)) == x
|
+-- Has a simpler equivalent? (stdlib, naive version)
| -> Oracle: my_impl(x) == reference(x)
|
+-- Repeated application should be stable? (formatting, normalization)
| -> Idempotency: f(f(x)) == f(x)
|
+-- Output has structural guarantees? (length, type, range, ordering)
| -> Invariant: check structural properties of output
|
+-- Hard to verify single output but can relate inputs?
| -> Metamorphic: f(transform(x)) relates to f(x) predictably
|
+-- Order of operations should not matter?
| -> Commutativity: f(g(x)) == g(f(x))
|
+-- Works on recursive/incremental data?
| -> Inductive: base case holds, step preserves property
|
+-- Output is hard to compute but easy to verify?
-> Hard-to-find/easy-to-verify: verify(solve(x)) == true
Most functions have 2-4 applicable property types. Start with easiest to express, then add more.
Invariant: len(sort(xs)) == len(xs). Most common starting point. Look for structural guarantees of output.
Idempotency: f(f(x)) == f(x). Applies to normalization, formatting, caching, PUT requests.
Round-trip: decode(encode(x)) == x. Highest-value property when applicable -- tests two functions simultaneously.
Oracle: my_sort(xs) == sorted(xs). Use when trusted reference exists. Oracle should be "so simple it's obviously correct."
Metamorphic: Define relations between executions. Example: sin(pi - x) == sin(x). Valuable when single-output verification is impossible.
Commutativity: f(g(x)) == g(f(x)). Applies when operations should be order-independent.
Inductive: Verify base case + step preservation. Natural for recursive structures.
Hard-to-find, easy-to-verify: product(factorize(n)) == n. Generate input, run algorithm, verify output cheaply.
integers().map(x => x * 2) for even numbersintegers(min=1) beats integers().filter(x > 0)| Strategy | How It Works | Used By | Trade-off |
|---|---|---|---|
| Type-based | Separate shrink function per type; returns list of simpler candidates | QuickCheck, PropEr, FsCheck | Explicit control but manual composition |
| Integrated | Generators produce shrink trees (rose trees); composition automatic | Hedgehog, fast-check, jqwik | Automatic but poor with dependent generators |
| Internal | Shrinks underlying byte stream, replays strategies | Hypothesis, rapid | Fully automatic, works with monadic bind |
Shrinking transforms [847, -23, 0, 445, 12] into [0, -1] -- minimal failing case. Without shrinking, PBT finds bugs but makes them hard to understand.
property "encode/decode round-trip" {
for_all input in generate(valid_document):
encoded = encode(input)
decoded = decode(encoded)
assert decoded == input
}
Collect statistics over 1000 runs; edge cases should appear in at least 5% of generated inputs.
For sequential operations or mutable state, see pbt-stateful skill.