プロパティベーステスト

ランダムな入力を大量に生成し、プログラムの不変条件が常に成り立つことを検証するテスト手法

テスト品質

プロパティベーステストとは

プロパティベーステスト (PBT) は、ランダムな入力を大量に生成し、プログラムの不変条件 (プロパティ) が常に成り立つことを検証するテスト手法である。Haskell の QuickCheck が起源。fast-check が TypeScript の代表的なライブラリ。

例ベーステスト vs プロパティベーステスト

観点 例ベース プロパティベース
入力 開発者が選んだ具体値 ランダムに自動生成
テスト数 数個〜数十個 数百〜数千
エッジケース 開発者が思いつく範囲 自動的に発見
検証 具体的な期待値 不変条件 (プロパティ)

fast-check での例

import fc from 'fast-check';

// プロパティ: ソートした配列は元の配列と同じ要素を持つ
test('sort preserves elements', () => {
  fc.assert(
    fc.property(fc.array(fc.integer()), (arr) => {
      const sorted = [...arr].sort((a, b) => a - b);
      expect(sorted.length).toBe(arr.length);
      expect(sorted.every(x => arr.includes(x))).toBe(true);
    })
  );
});

// プロパティ: JSON.parse(JSON.stringify(x)) === x (ラウンドトリップ)
test('JSON roundtrip', () => {
  fc.assert(
    fc.property(fc.jsonValue(), (value) => {
      expect(JSON.parse(JSON.stringify(value))).toEqual(value);
    })
  );
});

代表的なプロパティ

プロパティ
ラウンドトリップ encode → decode で元に戻る
冪等性 f(f(x)) === f(x)
不変条件 ソート後は昇順
可換性 a + b === b + a
モデルベース 実装とリファレンスが同じ結果

シュリンキング

テスト失敗: [42, -7, 0, 100, -3] で失敗
  ↓ シュリンキング (最小の反例を探索)
最小反例: [-1] で失敗
→ 負の数の処理にバグがある

fast-check は失敗した入力を自動的に縮小し、最小の反例を報告する。

DynamoDB のバリデーションテスト

test('全ての有効な注文がバリデーションを通過', () => {
  fc.assert(
    fc.property(
      fc.record({
        productId: fc.uuid(),
        quantity: fc.integer({ min: 1, max: 100 }),
        email: fc.emailAddress(),
      }),
      (order) => {
        expect(validateOrder(order).success).toBe(true);
      }
    )
  );
});

いつ使うか

ケース 推奨
パーサー、シリアライザー ✅ ラウンドトリップ
バリデーション ✅ 境界値の自動発見
アルゴリズム ✅ 不変条件の検証
UI テスト ❌ 例ベースの方が適切

さらに掘り下げるなら関連書籍が参考になる。

関連用語