| name | boundary-observation |
| description | 境界条件・エッジケースの観測。null/空/0/最大値/最小値/オーバーフロー/タイムゾーン等を網羅的にテスト。Use when: テスト設計、バリデーション実装、パーサー実装、日付/金額処理、例は通るが端で壊れる疑い、バグ修正後の再発防止。 |
Boundary Observation(境界条件観測)
目的
生成コードは「平均的なケースに強く、端に弱い」傾向がある。 このスキルは、入力空間の端での欠陥を発見し、自己欺瞞を崩す。
観測の恩恵
- "未知の未知"を減らす(狙っていない入力で壊す)
- 境界条件が「仕様に昇格」し、次回生成の精度が上がる
- バグが潜伏しやすい箇所(パーサ、日付、金額、状態遷移)が早く炙り出せる
Procedure
Step 1: 外部境界の特定
対象コードの「外部境界」を列挙する。
外部境界の例:
- API入力(リクエストパラメータ)
- DB境界(クエリ結果、NULL可能性)
- ファイル境界(サイズ、エンコーディング)
- 時間境界(タイムゾーン、DST、うるう年)
- 数値境界(0、負数、最大値、オーバーフロー)
Step 2: 境界値テストの設計
各外部境界に対して、以下のテストケースを設計する:
| カテゴリ | テストケース |
|---|---|
| 最小値 | 下限値、下限-1 |
| 最大値 | 上限値、上限+1 |
| 空/null | null、空文字、空配列 |
| 異常値 | 不正な型、不正なフォーマット |
Step 3: プロパティベーステスト(性質テスト)
重要なロジックに対して、期待値に依存しない性質テストを1本以上設計する。
検証すべき性質の例:
- 単調性: f(a) ≤ f(b) if a ≤ b
- 可逆性: decode(encode(x)) == x
- 冪等性: f(f(x)) == f(x)
- 保存則: sum(before) == sum(after)
- 交換律: f(a, b) == f(b, a)
Step 4: ファズテスト(必要に応じて)
以下のコンポーネントには特にファズテストが効く:
- パーサー
- デコーダー
- 正規化処理
- 文字コード変換
Step 5: 入力分布の観測(運用時)
実際にどんな値が来ているかを観測し、テストケースを補強する。
最小セット
- (B1) 境界値テスト:外部境界ごとに「最小・最大・空・異常」
- (B2) 重要ロジックに1つだけでも"性質テスト"を入れる
境界値カタログ
詳細は references/boundary-catalog.md を参照。
Outputs
- 境界値テストコード
- プロパティテストコード(QuickCheck / Hypothesis / fast-check 等)
- ファズテスト設定(必要に応じて)
Examples
日付処理の境界値テスト
# 境界値テスト
@pytest.mark.parametrize("date_str,expected", [
("2024-01-01", date(2024, 1, 1)), # 通常
("2024-02-29", date(2024, 2, 29)), # うるう年
("2023-02-29", ValueError), # 非うるう年 → 異常
("", ValueError), # 空文字
(None, TypeError), # null
("9999-12-31", date(9999, 12, 31)), # 最大年
])
def test_parse_date_boundaries(date_str, expected):
...
金額計算のプロパティテスト
# 性質テスト: 金額の加算は交換律を満たす
from hypothesis import given, strategies as st
@given(st.integers(min_value=0), st.integers(min_value=0))
def test_add_money_commutative(a, b):
assert add_money(a, b) == add_money(b, a)
# 性質テスト: 割引後の金額は元の金額以下
@given(st.integers(min_value=0, max_value=1000000), st.floats(min_value=0, max_value=1))
def test_discount_reduces_price(price, discount_rate):
result = apply_discount(price, discount_rate)
assert result <= price