mcpField
H
hypothesis-testing

hypothesis-testing

github.com/laurigates测试与质量5975 次安装1 天前更新
代码仓库

关于 hypothesis-testing

面向 Python 开发者的 Hypothesis 测试指南,可生成测试输入并验证代码属性。内容涵盖策略、配置、CI 集成与故障调试。

主要功能

  1. 01生成基于属性的测试
  2. 02内置与自定义策略
  3. 03按类型注解生成策略
  4. 04配置测试配置档与选项
  5. 05CI 集成与失败调试

适用场景

  1. 01检查数学不变量
  2. 02测试序列化往返
  3. 03探索 API 边界情况
  4. 04生成 NumPy 或 Django 测试数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/laurigates/claude-plugins --skill hypothesis-testing

安装后在 Claude Code 中输入/hypothesis-testing确认已加载。

附带文件REFERENCE.md

详细介绍

Hypothesis 属性测试

自动生成测试用例,以发现边界情况并验证代码的属性。

何时使用此技能

在以下情况下使用此技能……在以下情况下改用 property-based-testing……
使用 Hypothesis 编写 Python 属性测试使用非 Python 语言(TS、Rust 等)
为序列化往返生成测试数据编写基于示例的测试(使用 python-testing)
验证数学不变量(交换律、结合律)运行现有测试套件(使用 test-run)
使用 NumPy 或 Django 策略扩展设计整体测试策略(使用 test-consult)

何时使用 Hypothesis,何时使用基于示例的测试

在以下情况下使用 Hypothesis……在以下情况下使用基于示例的测试……
测试数学属性(交换律、结合律)测试已知的特定边界情况
需要测试许多有效输入已知具体取值的业务逻辑
验证序列化往返测试特定错误消息
发现无法预测的边界情况与外部系统集成
测试参数众多的 API测试 UI 行为

安装

uv add --dev hypothesis pytest

# Optional extensions
uv add --dev hypothesis[numpy]   # NumPy strategies
uv add --dev hypothesis[django]  # Django model strategies

配置

# pyproject.toml
[tool.hypothesis]
max_examples = 200
deadline = 1000

[tool.hypothesis.profiles.dev]
max_examples = 50
deadline = 1000

[tool.hypothesis.profiles.ci]
max_examples = 500
deadline = 5000
verbosity = "verbose"
# Activate profile
from hypothesis import settings, Phase
settings.load_profile("ci")  # Use in conftest.py

基本用法

from hypothesis import given, example, assume
import hypothesis.strategies as st

# Test a property
@given(st.integers(), st.integers())
def test_addition_commutative(a, b):
    assert a + b == b + a

# Add explicit edge cases
@given(st.integers())
@example(0)
@example(-1)
@example(2**31 - 1)
def test_with_explicit_examples(x):
    assert process(x) is not None

# Skip invalid inputs
@given(st.floats(allow_nan=False, allow_infinity=False),
       st.floats(allow_nan=False, allow_infinity=False))
def test_safe_divide(a, b):
    assume(b != 0)
    result = a / b
    assert isinstance(result, float)

核心策略

import hypothesis.strategies as st

# Primitives
st.integers()                          # Any integer
st.integers(min_value=0, max_value=100)  # Bounded
st.floats(allow_nan=False)             # Floats without NaN
st.booleans()                          # True/False
st.text()                              # Unicode strings
st.text(min_size=1, max_size=50)       # Bounded strings
st.binary()                            # Bytes

# Collections
st.lists(st.integers())                # List of ints
st.lists(st.text(), min_size=1)        # Non-empty list
st.dictionaries(st.text(), st.integers())  # Dict
st.tuples(st.integers(), st.text())    # Fixed tuple

# Special types
st.emails()                            # Valid emails
st.uuids()                             # UUID objects
st.datetimes()                         # datetime objects

# Choices
st.sampled_from(["a", "b", "c"])       # Pick from list
st.one_of(st.integers(), st.text())    # Union type
st.none() | st.integers()              # Optional int
自定义组合策略
from hypothesis.strategies import composite

@composite
def users(draw):
    return {
        "id": draw(st.integers(min_value=1)),
        "name": draw(st.text(min_size=1, max_size=50)),
        "email": draw(st.emails()),
        "active": draw(st.booleans())
    }

@given(users())
def test_user_validation(user):
    assert user["id"] > 0
    assert "@" in user["email"]
根据类型注解生成
from hypothesis import given
from hypothesis.strategies import from_type
from dataclasses import dataclass

@dataclass
class Config:
    name: str
    port: int
    debug: bool

@given(from_type(Config))
def test_config(config: Config):
    assert isinstance(config.name, str)
    assert isinstance(config.port, int)

常见属性模式

# 1. Round-trip (encode/decode)
@given(st.text())
def test_json_roundtrip(data):
    assert json.loads(json.dumps(data)) == data

# 2. Idempotency (applying twice = applying once)
@given(st.lists(st.integers()))
def test_sort_idempotent(items):
    assert sorted(sorted(items)) == sorted(items)

# 3. Invariant preservation
@given(st.lists(st.integers()))
def test_sort_preserves_length(items):
    assert len(sorted(items)) == len(items)

# 4. Oracle (compare implementations)
@given(st.integers(min_value=0, max_value=20))
def test_fibonacci(n):
    assert fast_fib(n) == slow_fib(n)

CI 集成

# .github/workflows/test.yml
- name: Run hypothesis tests
  run: |
    uv run pytest \
      --hypothesis-show-statistics \
      --hypothesis-profile=ci \
      --hypothesis-seed=${{ github.run_number }}

- name: Upload hypothesis database
  uses: actions/upload-artifact@v4
  if: failure()
  with:
    name: hypothesis-examples
    path: .hypothesis/

Agentic 优化

场景命令
快速检查pytest -x --hypothesis-seed=0 -q
快速失败pytest --hypothesis-profile=dev -x --tb=short
CI 模式pytest --hypothesis-profile=ci --hypothesis-show-statistics
可复现pytest --hypothesis-seed=42
调试失败用例pytest -x -s --hypothesis-verbosity=debug
不进行缩减在 @settings 中添加 phases=[Phase.generate]

快速参考

# Core decorators
@given(strategy)              # Generate test inputs
@example(value)               # Add explicit test case
@settings(max_examples=500)   # Configure behavior

# Key settings
assume(condition)             # Skip invalid inputs
note(message)                 # Add debug info to failure
target(value)                 # Guide generation toward value

有关高级模式(有状态测试、递归数据、设置)、最佳实践和调试指南,请参阅 REFERENCE.md。

评论

支持 Markdown