mrkeyoor.com_
Sun 20 Sept 17:51 UTC
PyPITestingupdated 20 Sept 2026

hypothesis review

Hypothesis 6.165.10 is a property-based testing library for Python. A strategy describes valid inputs, `@given` runs the test over generated cases, and the shrinker searches for a smaller counterexample after a failure. Its local example database replays useful failures before exploring new inputs, and state machines cover sequences of operations. The current patch corrects `from_regex` for mixed negative classes, `re.ASCII` complements and case folding, plus optional subpatterns excluded by a custom alphabet. Our installed distribution included py.typed and compiled extensions.

Verdict

Hypothesis 6.165.10 installed in 0.3 seconds and used 5 MB across two packages in our sandbox, with typed APIs, compiled extensions, and no audit findings. Add it where you can state a property clearly; use plain pytest cases when the behavior is already best described by a short example table.

We installed it

Lab card: what happened when we installed hypothesisScreenshot of hypothesis documentation
Install✓ · 0.3s2 packages on disk · 5 MB
Importimport hypothesis in 0.60s · compiled extensions · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does hypothesis install cleanly?

Yes. In a fresh container with an empty cache, pip install hypothesis finished in 0.3s, leaving 2 packages and 5 MB on disk. pip-audit reported no known vulnerabilities.

What does hypothesis need to run?

Python >=3.10, and a platform wheel with compiled extensions. In our run import hypothesis succeeded in 0.60s, and the package ships py.typed for type checkers.

hypothesis or pytest: which should you use?

pytest: Use parametrization when a small named table of cases is the clearest specification. Hypothesis 6.165.10 installed in 0.3 seconds and used 5 MB across two packages in our sandbox, with typed APIs, compiled extensions, and no audit findings.

When should you not use hypothesis?

The specification is a short list of named input-output pairs. pytest.mark.parametrize expresses those cases more directly.

API stability5/5The long 6.x line retains `@given`, `@example`, settings profiles, the strategies namespace, and RuleBasedStateMachine as its central interfaces. Releases frequently alter generation and shrinking details because finding previously missed cases is part of the product. Version 6.165.10 changes only incorrect `from_regex` generation for three documented forms. Source code usually stays unchanged, although a corrected strategy can expose inputs that an earlier patch never produced.
Docs5/5The documentation has separate tutorials, how-to guides, explanations, integration notes, and API references. It describes shrinking, the example database, health checks, profiles, stateful tests, strategy design, and pytest fixture scope. Changelog entries are unusually specific: 6.165.10 names mixed negative classes, ASCII complement behavior, case folding, and zero-repeat branches with restricted alphabets. That detail is enough to decide whether a patch changes a test's explored space.
Maintenance5/5Version 6.165.10 was published on August 16, 2026, and GitHub showed the repository pushed on August 25, 2026. It is unarchived with 49 open issues and pull requests. Three user-visible regex defects received one focused patch with issue references, while adjacent August releases improved shrinking, target-phase behavior, wheel platforms, and Python 3.15 support. The cadence shows active correction rather than release-number churn alone.
Ecosystem5/5Registry data records 12,182,027 weekly downloads, and the repository has 8,913 GitHub stars. Hypothesis plugs into pytest automatically and supplies strategies or extras for Python's built-in types, NumPy, pandas, Django, dates, regexes, functions, and state machines. Schemathesis and other domain tools build generated testing on top of it. Teams can add one property beside existing examples without replacing their runner or test suite.

Use it if

  • The requirement is an invariant or round trip that should hold over many integers, strings, collections, or structured records.
  • Parser, serializer, Unicode, or floating-point edge cases are broader than a maintained table of examples.
  • A queue, cache, or protocol can be tested as generated operations with an invariant checked after each step.
  • A failing generated input should be reduced to a small counterexample that can become a readable regression test.
Skip it if

Setup reality

We installed Hypothesis 6.165.10 in a fresh Python 3.12 Bookworm sandbox. pip took 0.3 seconds and left two packages using 5 MB. The package metadata declares 37 direct dependencies and Python 3.10 or later. import hypothesis worked in 0.60 seconds. The distribution ships py.typed and compiled .so files. pip-audit found zero known vulnerabilities, while the measured package metadata did not provide a license value.

Installing it registers a pytest plugin and introduces local test state. Hypothesis normally stores interesting and failing cases under .hypothesis; it replays those before generating fresh data. That database helps locally but should not be the only record of a repaired bug. Preserve the printed reproduction details from CI, then add a readable @example once the cause is understood. Disposable runners need an explicit database policy if replay across jobs matters.

One decorated test runs the body repeatedly. The default deadline can expose accidental slowness, and heavy filtering can trigger health checks after too many rejected values. Put constraints into strategies instead of relying on assume() for most candidates. A function-scoped pytest fixture is constructed once around the generated test, not once per example, so reset mutable state inside the body or keep external integration work in ordinary tests.

Profiles control example counts, deadlines, phases, and reproducibility for different environments. Optional extras cover NumPy, pandas, Django, and the ghostwriter command. Version 6.165.10 is particularly relevant to regex strategies: from_regex(r'[\W_]'), complements under re.ASCII, and patterns with a zero-repeat branch plus restricted alphabet now follow Python's regex rules. Pin this patch if those generators protect a parser or validator.

Patterns

Check a binary round trip round-trip

from hypothesis import given, strategies as st

@given(st.binary())
def test_round_trip(payload: bytes) -> None:
    assert decode(encode(payload)) == payload

A round trip has an expected result for every generated byte string. Also test format requirements so matching encoder and decoder bugs cannot cancel out.

Build valid dataclass instances structured-records

from dataclasses import dataclass
from hypothesis import given, strategies as st

@dataclass
class LineItem:
    sku: str
    quantity: int

line_items = st.builds(
    LineItem,
    sku=st.text(min_size=1, max_size=20),
    quantity=st.integers(min_value=1, max_value=100),
)

@given(line_items)
def test_total_is_nonnegative(item: LineItem) -> None:
    assert price(item) >= 0

`st.builds` invokes the real constructor. Express domain limits in its field strategies so generation does not discard mostly invalid objects.

Draw a valid slice range dependent-values

from hypothesis import given, strategies as st

@st.composite
def slices(draw):
    values = draw(st.lists(st.integers(), min_size=1, max_size=20))
    start = draw(st.integers(0, len(values) - 1))
    stop = draw(st.integers(start, len(values)))
    return values, start, stop

@given(slices())
def test_slice_size(case) -> None:
    values, start, stop = case
    assert len(values[start:stop]) == stop - start

A composite strategy fits because the index bounds depend on the generated list. Simple draw order also gives the shrinker fewer dependencies to untangle.

Load explicit CI settings ci-profile

import os
from hypothesis import settings

settings.register_profile(
    'ci',
    max_examples=500,
    deadline=None,
    derandomize=True,
)
if os.environ.get('CI'):
    settings.load_profile('ci')

Disabling the deadline accepts variable test time. `derandomize=True` aids repeatability but stops each run from exploring a fresh random sequence.

Keep known edge cases in source regression-example

from hypothesis import example, given, strategies as st

@given(st.text())
@example('')
@example('e\u0301')
def test_slug_has_no_spaces(value: str) -> None:
    assert ' ' not in slugify(value)

`@example` runs readable cases before generated ones. Keep it after a fix even if the local example database can replay the same input.

Generate complete regex matches regex-fullmatch

import re
from hypothesis import given, strategies as st

slugs = st.from_regex(
    re.compile(r'[a-z][a-z0-9-]{0,31}', re.ASCII),
    fullmatch=True,
)

@given(slugs)
def test_slug_parser(value: str) -> None:
    assert parse_slug(value) == value

`fullmatch=True` prevents unrelated prefix or suffix text. Version 6.165.10 fixes several ASCII and negative-category generation errors.

Constrain values before filtering avoid-filtering

from hypothesis import assume, given, strategies as st

@given(st.integers(min_value=1))
def test_reciprocal(value: int) -> None:
    assert 0 < 1 / value <= 1

@given(st.integers(), st.integers())
def test_difference(left: int, right: int) -> None:
    assume(left != right)
    assert left - right != 0

A bounded integer strategy wastes no draws. Use `assume` when direct construction of the valid relationship would make the test harder to read.

Explore cache operation sequences state-machine

from hypothesis import strategies as st
from hypothesis.stateful import RuleBasedStateMachine, invariant, rule

class CacheMachine(RuleBasedStateMachine):
    def __init__(self):
        super().__init__()
        self.cache = Cache(capacity=3)

    @rule(key=st.text(min_size=1), value=st.integers())
    def put(self, key, value):
        self.cache.put(key, value)

    @invariant()
    def stays_within_capacity(self):
        assert len(self.cache) <= 3

TestCache = CacheMachine.TestCase

Expose `TestCase` at module scope for pytest collection. The invariant runs throughout generated operation sequences, not only after one fixed scenario.

Replay a printed reproduction blob replay-ci-failure

from hypothesis import given, reproduce_failure, strategies as st

@reproduce_failure('6.165.10', b'PASTE_BLOB_FROM_CI')
@given(st.lists(st.integers()))
def test_parser(values) -> None:
    assert parse(dump(values)) == values

A reproduction blob is tied to a Hypothesis version. Use it for diagnosis, then replace it with a readable `@example` before merging.

Generate NumPy arrays and shapes numpy-arrays

import numpy as np
from hypothesis import given
from hypothesis.extra.numpy import array_shapes, arrays

@given(arrays(
    dtype=np.float64,
    shape=array_shapes(min_dims=1, max_dims=2, max_side=8),
))
def test_normalize_keeps_shape(values) -> None:
    assert normalize(values).shape == values.shape

Float generation may include NaN and infinity. Exclude them only if the function's stated input contract rejects them.

Search toward larger cases target-extremes

from hypothesis import given, strategies as st, target

@given(st.lists(st.integers(), max_size=200))
def test_deduplicate(values) -> None:
    target(len(values), label='input length')
    result = deduplicate(values)
    assert len(result) <= len(values)

`target` biases exploration toward higher observed scores while the property still controls pass or fail. Labels separate multiple optimization goals.

Draw an index after its list interactive-draws

from hypothesis import given, note, strategies as st

@given(st.data())
def test_lookup(data) -> None:
    values = data.draw(st.lists(st.integers(), min_size=1), label='values')
    index = data.draw(st.integers(0, len(values) - 1), label='index')
    note(f'index={index}, size={len(values)}')
    assert lookup(values, index) == values[index]

`st.data()` permits draws whose shape depends on values already seen in the test body. Labels and notes appear in useful failure output.

Alternatives

PackageRegistryPick it when
pytestPyPIUse parametrization when a small named table of cases is the clearest specification.
FakerPyPIUse it to produce plausible fixture and demo records rather than minimized adversarial cases.
schemathesisPyPIUse it when OpenAPI or GraphQL should drive generated requests and response checks.
crosshair-toolPyPITry it when contracts and symbolic path exploration fit the function better than generated sampling.

More testing guides

pytest · chai · vitest · jsdom · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.