Property-Based Testing with Hypothesis: Invariants to State Machines

Hypothesis automates test input generation to verify function invariants (e.g., clamp stays in bounds), parser agreement via differential testing, stats under transformations, and bank account consistency via stateful rules—shrinking failures to minimal counterexamples.

Generate Structured Inputs with Composite Strategies

Define strategies to produce realistic test data: bounds ensures lo <= hi by swapping tuples of integers(-10k,10k); int_like_strings builds strings with optional sign, 1-300 digits, and 0-5 whitespace chars around; sorted_lists creates lists of 0-200 integers(-10k,10k) pre-sorted. These constrain exploration to meaningful edge cases like huge numbers (len>2000 rejects as 'too_big') or malformed inputs, enabling targeted property checks without manual examples.

For safe_parse_int, reject non-integers via regex [+-]?\d+, strip whitespace, cap digit length at 2000, and safely convert—alternative parser manually accumulates value to avoid overflow, agreeing on all int_like_strings inputs.

Verify Pure Function Properties and Idempotence

Clamp satisfies lo <= clamp(x,lo,hi) <= hi and idempotence clamp(clamp(x,lo,hi),lo,hi) == clamp(x,lo,hi) for x in -50k..50k, run 300 examples with max_examples=300, suppress_health_check=[HealthCheck.too_slow].

normalize_whitespace collapses splits to single spaces, idempotent even with added leading/trailing whitespace/tabs/newlines: @example(" a\t\tb\nc ") confirms, 250 examples.

Merge sorted lists matches sorted(a+b) reference and stays non-decreasing (all(xs[i] <= xs[i+1])), 250 examples—differential testing catches implementation bugs by comparing to trusted merge_sorted_reference.

Apply Differential, Metamorphic, and Stateful Testing

Differential: Two parsers agree on success/failure for int_like_strings (250 examples, deadline=200ms); reject non-matches or oversized correctly.

Metamorphic: variance unchanged under shift variance([x+k for x in xs]) == variance(xs) (rel_tol=1e-12), always >=0, 0 for n<2; target via target(variance(xs)) focuses high-variance cases, lists 0-80 elems(-1k..1k), phases=generate,shrink.

Stateful: BankMachine simulates deposits/withdrawals: @initialize checks balance=0; @rule(amt=1..10k) for deposit; @precondition(balance>0) @rule(amt=1..10k) assume(amt<=balance) for withdraw. Invariants: balance >=0, replay_balance() == balance (replays ledger). Hypothesis sequences operations, shrinking violating sequences to minimal failures.

Run via pytest: all pass verifies correctness across 100s examples, shrinking exposes bugs like negative balance or ledger mismatch.

Summarized by x-ai/grok-4.1-fast via openrouter

6281 input / 1442 output tokens in 7734ms

© 2026 Edge