Property-based testing
Example-based testing asserts one input → one expected output. Property-based testing (PBT) inverts it: you state an invariant that must hold for all inputs — “LRU cache size never exceeds capacity”, “serialize(parse(x)) == x”, “sort(l) is a permutation of l” — and the framework generates hundreds of random cases trying to break it, shrinking any failure down to the minimal reproducing input. One property replaces a wall of example tests, and the adversarial generator finds edge cases humans forget (empty input, unicode, off-by-one boundaries, negative zero).
The canonical lineage:
- QuickCheck (Claessen & Hughes, 2000, Haskell) — originated the idea: properties as executable specs, random generation, shrinking. Every later tool copies this shape.
- Hypothesis (Python, MacIver) — the production workhorse: strategy-based generation, database of failing examples replayed across runs, deterministic replay of shrunk failures. Also the engine under
pytest’s ecosystem and the reference implementation others now build on. - Hegel (hegel.dev, 2026) — the “universal PBT engine” claim: one protocol (hegel-core), language bindings for Rust/Go/TypeScript/OCaml, all built on Hypothesis — same generators and shrinking across languages, so a protocol fuzzer and its implementations in different languages share test cases. @GeoffreyHuntley’s timeline reminder is the capture that opened this trail.
- Also in the family: fast-check (JS/TS), scalacheck (Scala), proptest (Rust, pre-Hegel), falsum… the pattern is portable everywhere.
Why it matters for agent-era code, tying into Agent tests: a tautological test restates the implementation and never fails; a property cannot be tautological by construction — it’s a claim about the input space, not a replay of the code’s output. For LLM-written code the highest-value properties are the cheap ones to state: roundtrips, invariants, idempotence, metamorphic relations. And failure-mode-first thinking (from the same AGENTS.md rule) is exactly “list the invariants, then generate against them.”
Practical notes:
- Properties complement, not replace, E2E (Agent tests): PBT nails pure logic and parsers/serializers; E2E nails the wiring. The Golden repo test section wants both.
- Shrinking is the feature that pays the bills — a 3-element minimal counterexample beats a random 10k-element one.
- Keep generation realistic or you drown in “not-a-bug” failures; constrain generators to the valid input domain (Hypothesis strategies compose for exactly this).
Sources (captured 2026-09-26): clippings/x-2103800381415551009.md (tweet) · clippings/hegel-dev.md (hegel.dev) · hegel-rust (OSS).
Growing
- Try Hypothesis on a real ocli/cldb function (a parser or date util) — feel the shrink cycle once before theorizing.
- Hegel’s cross-language protocol angle: relevant to testing the same wire format consumed by Python + Go services at work?