hypothesis-testing について
Hypothesisを使うPython開発者向けのテストガイドです。入力データの生成、戦略や設定、CIでの実行、失敗時のデバッグを扱います。
主な機能
- 01プロパティベーステストの生成
- 02組み込み・カスタム戦略
- 03型注釈からの戦略生成
- 04プロファイルとテスト設定
- 05CI連携と失敗時のデバッグ
活用シーン
- 01数学的な不変条件の確認
- 02シリアライズ往復のテスト
- 03APIの境界ケースの探索
- 04NumPyや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/
エージェント向けの最適化
| コンテキスト | コマンド |
|---|---|
| クイックチェック | 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 対応