Property based testing
Skill TomerAberbach/claude-config/skills/property-based-testing
🤖 My Claude Code config!
npx -y skills add TomerAberbach/claude-config --skill property-based-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when writing, reviewing, or modifying property-based tests, such as those using `fast-check`, or when asked to add test coverage.
SKILL.md
4.5 KB, as published. Nobody here has run it
Before starting, load /authoring-tests.
Adhere to these principles when writing property-based tests:
-
Write focused properties with focused arbitraries to properly explore slices of the input space.
-
Prefer arbitraries that generate the desired input space directly instead of generating a larger input space and then filtering it down.
-
Keep assertions focused only on what's straightforward to infer from the input arbitraries. It's often infeasible assert the full value. If no assertion seems feasible, then the input space is too broad.
-
NEVER unnecessarily constrain an arbitrary. Only add constraints if it's necessary to satisfy the property or resolve performance problems.
Common properties
-
"Output is always/never X for all inputs". Example: for any number
n,Math.floor(n)is an integer. -
"When input is X, then output is always/never Y". Example: for any array
datawith no duplicates, the result of removing duplicates fromdataisdataitself. -
"Complex implementation X is equivalent to simpler implementation Y". Example: "
cis contained inside sorted arraydatafor binary search" is equivalent to "cis contained insidedatafor linear search". -
Totality: function returns (doesn't throw) for all valid inputs. Example:
JSON.parse(JSON.stringify(x))never throws for serializablex. -
Deterministic: always returns the same output for the same input. Example: for any date
d,formatDate(d)always returns the same string. -
Side-effect free: does not mutate the input or non-local state. Example: for any array
data,sorted(data)leavesdataunchanged. -
Bounded output: output is always within a known range. Example:
clamp(x, low, high)always returns a value betweenlowandhigh. -
Structural invariant: output always has a guaranteed shape/structure. Example:
partition(predicate, data)always produces two arrays whose combined length equalsdata.length. -
Closure under operation: applying
fto valid inputs always produces a valid output of the same type/domain. Example:add(positiveInt, positiveInt)is always a positive integer. -
Identity element: there exists an input that leaves output unchanged. Example:
concat(xs, [])equalsxs. -
Absorption/annihilation: certain inputs collapse the result regardless of the other. Example:
and(false, x)is alwaysfalse. -
Idempotent: running twice is the same as running once, either in its effect on non-local state or when passing its first output as its second input. Example: for any array
data,sort(sort(data))equalssort(data). -
Commutative: rearranging argument order doesn't affect output. Example: for any numbers
aandb,add(a, b)equalsadd(b, a). -
Associative: regrouping arguments for multiple calls doesn't affect output. Example:
concat(concat(a, b), c)equalsconcat(a, concat(b, c)). -
Distributive:
f(a ∪ b) === f(a) ∪ f(b). Example:map(f, [...xs, ...ys])equals[...map(f, xs), ...map(f, ys)]. -
Inverse/symmetry/roundtrips:
fandgare inverses of each other. Example:decode(encode(x))equalsx. -
Transitivity: if
f(a, b)andf(b, c), thenf(a, c). Example: ifisAncestor(a, b)andisAncestor(b, c), thenisAncestor(a, c). -
Monotonic: if input increases (or decreases), output always changes in the same direction. Example: sorting more elements never produces fewer elements.
-
Consistent ordering: if
acomes beforebin the input, the relative order is preserved in the output. Example: a stable sort never reorders equal elements relative to each other. -
Prefix/suffix closure: if
f(x)holds, it holds for any sub-input, or conversely, for any superset. Example: ifisValid(data)thenisValid(data.slice(0, n))for alln.
These aren't exhaustive. Reason from first principles when none fits cleanly.
fast-check tips
-
Use
fc.clone(arb, count)to produce multiple equal value instead of usingstructuredCloneorJSON.parse(JSON.stringify(...)). -
Use
fc.uniqueArray(arb, { minLength, maxLength })to produce unique values instead of usingfc.tuple(...arbs).filter(...).