Claude-skill-registry hypothesis-strategies
Custom Hypothesis strategy patterns for property-based testing. Activated when designing test data generators or property tests.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/hypothesis-strategies" ~/.claude/skills/majiayu000-claude-skill-registry-hypothesis-strategies && rm -rf "$T"
manifest:
skills/data/hypothesis-strategies/SKILL.mdsource content
Hypothesis strategies
Purpose
Guide for designing custom Hypothesis strategies for property-based testing. Covers strategy composition, custom generators, and shrinking behavior.
When to use
This skill activates when:
- Creating custom data generators
- Composing complex test inputs
- Designing property-based tests
- Debugging shrinking behavior
- Generating domain-specific data
Core concepts
Basic strategies
from hypothesis import strategies as st # Basic types text = st.text() integers = st.integers() floats = st.floats() booleans = st.booleans() # Constrained types positive_ints = st.integers(min_value=1) short_text = st.text(max_size=100) ascii_text = st.text(alphabet=string.ascii_letters)
Strategy composition
from hypothesis import strategies as st # Combine strategies point = st.tuples(st.floats(), st.floats()) person = st.fixed_dictionaries({ 'name': st.text(min_size=1), 'age': st.integers(min_value=0, max_value=150), }) # One of multiple options value = st.one_of(st.text(), st.integers(), st.booleans()) # Optional values maybe_text = st.text() | st.none()
Custom strategies
Using @composite
from hypothesis import strategies as st from hypothesis.strategies import composite, DrawFn @composite def valid_identifiers(draw: DrawFn) -> str: """Generate valid Python identifiers.""" first = draw(st.sampled_from(string.ascii_letters + '_')) rest = draw(st.text( alphabet=string.ascii_letters + string.digits + '_', max_size=30 )) return first + rest @composite def valid_emails(draw: DrawFn) -> str: """Generate valid email addresses.""" local = draw(st.text( alphabet=string.ascii_lowercase + string.digits + '._', min_size=1, max_size=64, )) domain = draw(st.text( alphabet=string.ascii_lowercase, min_size=1, max_size=20, )) tld = draw(st.sampled_from(['com', 'org', 'net', 'io'])) return f"{local}@{domain}.{tld}"
Building from existing strategies
@composite def config_dicts(draw: DrawFn) -> dict: """Generate valid configuration dictionaries.""" name = draw(valid_identifiers()) options = draw(st.lists(st.text(), max_size=10)) enabled = draw(st.booleans()) timeout = draw(st.integers(min_value=1, max_value=3600) | st.none()) config = { 'name': name, 'options': options, 'enabled': enabled, } if timeout is not None: config['timeout'] = timeout return config
Recursive strategies
@composite def nested_dicts(draw: DrawFn, max_depth: int = 3) -> dict: """Generate nested dictionary structures.""" if max_depth <= 0: # Base case: simple values only return draw(st.fixed_dictionaries({ 'value': st.one_of(st.text(), st.integers(), st.booleans()) })) # Recursive case return draw(st.fixed_dictionaries({ 'value': st.one_of(st.text(), st.integers(), st.booleans()), 'children': st.lists( st.deferred(lambda: nested_dicts(max_depth=max_depth - 1)), max_size=3 ) }))
Strategy for domain types
Dataclass strategies
from dataclasses import dataclass from hypothesis import strategies as st @dataclass class User: id: int name: str email: str active: bool @composite def users(draw: DrawFn) -> User: """Generate valid User instances.""" return User( id=draw(st.integers(min_value=1)), name=draw(st.text(min_size=1, max_size=100)), email=draw(valid_emails()), active=draw(st.booleans()), )
Enum strategies
from enum import Enum class Status(Enum): PENDING = 'pending' ACTIVE = 'active' COMPLETED = 'completed' # Generate any status status_strategy = st.sampled_from(Status) # Generate only certain statuses active_statuses = st.sampled_from([Status.PENDING, Status.ACTIVE])
Using with @given
Basic usage
from hypothesis import given @given(users()) def test_user_has_valid_id(user: User): """User IDs are always positive.""" assert user.id > 0 @given(st.lists(users(), min_size=1)) def test_user_list_not_empty(users: list[User]): """Generated user lists have at least one user.""" assert len(users) >= 1
With assume
from hypothesis import given, assume @given(st.integers(), st.integers()) def test_division(a: int, b: int): """Test division with valid divisor.""" assume(b != 0) result = a / b assert result * b == a
Multiple strategies
@given( user=users(), permissions=st.lists(st.sampled_from(['read', 'write', 'admin'])), ) def test_user_with_permissions(user: User, permissions: list[str]): """Test user with various permission sets.""" result = apply_permissions(user, permissions) assert all(p in result.permissions for p in permissions)
Controlling shrinking
@composite def ids_with_checksum(draw: DrawFn) -> str: """Generate IDs where parts must stay together during shrinking.""" # Use map to preserve relationships during shrinking parts = draw(st.lists(st.integers(min_value=0, max_value=9), min_size=4, max_size=4)) checksum = sum(parts) % 10 return ''.join(map(str, parts)) + str(checksum)
Debugging strategies
# See what a strategy generates from hypothesis import settings, Verbosity @settings(verbosity=Verbosity.verbose) @given(users()) def test_debug_user_generation(user: User): """See generated values.""" pass # Values printed to output # Generate examples without running tests from hypothesis import find # Find example that satisfies predicate example = find(users(), lambda u: u.active and len(u.name) > 5)
Checklist
- Strategy generates valid domain data
- Edge cases covered (empty, max size, special values)
- Constraints properly enforced
- Shrinking produces meaningful minimal examples
- Strategy is composable with others
Additional resources: