CtrlK
BlogDocsLog inGet started
Tessl Logo

evilissimo/property-based-testing

Generates **property-based tests** that use randomized input generation to validate invariants and contracts (rather than hand-picked examples). Triggers when the conversation involves: PBT frameworks (Hypothesis library for Python, fast-check for TypeScript, proptest for Rust, rapid for Go, RapidCheck for C++); concepts like invariants, contracts, round-trip symmetry, encode/decode, serialize/deserialize, generative testing, or shrinking; or requests to find edge cases that example-based tests miss — e.g., "find edge cases automatically", "test all possible inputs", "verify this property holds". Does NOT trigger for: writing regular example-based unit tests, debugging, CI/CD setup, UI/component testing, or integration/E2E testing. Identifies up to 7 property patterns (round-trip, idempotence, invariance, metamorphic, inverse, ordering, no-crash), designs input generators, writes property tests, and extracts regression tests from failures.

91

1.11x
Quality

90%

Does it follow best practices?

Impact

94%

1.11x

Average score across 5 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

python.mdreferences/

Python: Hypothesis

Dependency: pip install hypothesis (add pytest for running tests)

Core API

from hypothesis import given, assume, strategies as st

Common Strategies (Generators)

st.integers()                  # Any integer
st.integers(min_value=0, max_value=100)  # Constrained
st.floats()                    # Any float (includes NaN, inf)
st.floats(allow_nan=False)     # Finite floats only
st.text()                      # Any string (including empty)
st.text(alphabet="abc", min_size=1)  # Strings from a specific alphabet
st.booleans()                  # True or False
st.none()                      # Always None
st.just(42)                    # Always 42 (constant)
st.sampled_from(["a", "b"])    # Pick randomly from a list
st.lists(st.integers())        # Lists of integers
st.lists(st.integers(), min_size=1, max_size=10)  # Sized lists
st.dictionaries(st.text(), st.integers())  # Dicts
st.tuples(st.text(), st.integers())       # Tuples
st.one_of([st.none(), st.text()])         # Union types
st.builds(MyClass, name=st.text(), age=st.integers(0, 120))  # Construct objects

Writing Property Tests

@given(st.lists(st.integers()))
def test_sort_length_invariant(lst):
    original_len = len(lst)
    result = sorted(lst)
    assert len(result) == original_len

@given(st.lists(st.integers()))
def test_sort_ordered(lst):
    result = sorted(lst)
    for i in range(len(result) - 1):
        assert result[i] <= result[i + 1]

Filtering with assume

Skip invalid input combinations:

@given(st.integers(), st.integers())
def test_divide(a, b):
    assume(b != 0)
    assert a / b * b == a  # Approximate

Composition for Complex Objects

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

@given(st.builds(User,
    name=st.text(min_size=1, max_size=50),
    age=st.integers(min_value=0, max_value=150)))
def test_user_properties(user):
    assert len(user.name) > 0

Custom Strategies with .map() and .filter()

# Map: transform generated values
even_ints = st.integers().map(lambda x: x * 2)

# Filter: only keep values matching predicate
nonzero = st.integers().filter(lambda x: x != 0)

# Combine
positive_evens = st.integers(min_value=1).map(lambda x: x * 2)

Testing for Exceptions

from hypothesis import given, strategies as st

@given(st.integers())
def test_divide_by_zero_raises(x):
    assume(x != 0)
    with pytest.raises(ZeroDivisionError):
        1 / (x - x)  # This will always fail since we assume x != 0

Settings Decorator

from hypothesis import settings

@given(st.lists(st.integers()))
@settings(max_examples=500)  # Default is 100
def test_with_more_iterations(lst):
    ...

@given(st.lists(st.integers()))
@settings(max_examples=10)  # Fast check for CI
def test_quick(lst):
    ...

Reproducing a Failure

When Hypothesis reports a failure, it gives you a seed:

Falsifying example: test_sort(
    lst=[3, 1, 2],
)

Use @seed(42) or --hypothesis-seed=42 to reproduce:

from hypothesis import seed

@given(st.lists(st.integers()))
@seed(12345)
def test_sort_reproducible(lst):
    ...

Running

pytest test_stock.py -v                    # All tests
pytest test_stock.py -k "property" -v      # Filter by name
pytest --hypothesis-show-statistics        # Show generated value stats

Hypothesis Database

Hypothesis saves failing examples in .hypothesis/examples/. This means failures from a previous run are replayed on subsequent runs — you don't lose found bugs.

SKILL.md

tessl.json

tile.json