決定表テストと構造テストの関係性
ソフトウェアテストの一つに決定表テスト(Decision Table)という手法があります。実務ではExcelで作成されるケースが多く、視覚的に分かりやすいため、ドメイン知識に習熟した非技術者でも扱いやすいのが特徴です。
一方で、実装されたコードの動作を確認するには、構造テスト(例えばユニットテストなど)が使われます。(もし構造テストについて詳しく知りたい場合、過去にソフトウェアテストのカバレッジ基準を順を追って理解する:Line Coverageから MC/DCまででまとめているものをご参照ください)
一見するとこの2つの手法は全く別物のように見えますが、同じロジックに対してなぜ異なるアプローチが必要なのかを理解することは、実務においても非常に重要です。本記事では、その理由と背景をまとめます。
決定表の基本構造
決定表は非常にシンプルな構造を持ちます。縦軸に条件、最終行に結果を記載し、それぞれの条件に対する入力値の組み合わせをバリアント(Variant)として横方向に記述します。
条件 | Variant 1 | Variant 2 | Variant 3 |
---|---|---|---|
年齢 > 18 | true | false | true |
IDあり | true | true | false |
→ 結果 | OK | NG | NG |
- conditions(条件):判断に使う前提・入力項目(行)
- variants(バリアント):条件の組み合わせによるテストケース(列)
このような表を使ってテストケースを整理し、網羅的なテストができるようにします。
構造テストと同様に、目的に応じたテスト戦略も存在します。
決定表におけるテスト戦略比較
戦略名 | 条件全網羅 | 結果全網羅 | 明示ケース全網羅 | 条件が効くか確認(MC/DC) |
---|---|---|---|---|
all-conditions | ✅ | ❌ | ❌ | ❌ |
all-decisions | ❌ | ✅ | ❌ | ❌ |
all-explicit-variants | ✅(明示分) | ✅ | ✅ | ❌ |
MC/DC(条件独立性) | ✅(必要条件のみ) | ✅ | ❌ | ✅ |
なぜアプローチが変わるのか?
ここまで読むと、
「決定表で仕様を確認 → 仕様を元にコードを実装 → 構造テストで動作確認、までできるなら決定表ベースのテストっていらないのでは?」
と思うかもしれません。
しかし、同じロジックをテストしているように見えても、着目点が異なるため、アプローチも異なるのです。
目的 | 適したテスト手法 |
---|---|
仕様や判断の抜けを防ぎたい | モデルベース(決定表) |
実装にミスがないか確認したい | 構造テスト |
決定表テストはなぜ必要?
たとえ決定表を使って仕様を整理し、構造テストをしっかり行ったとしても、決定表ベースのテストには以下のような独自の価値があります。
1. 実装が仕様通りとは限らない
- 実装時に仕様の見落としや誤解が起きることがある。
- 決定表テストは「仕様に忠実にテスト」するため、意図された判断ロジックを確認できる。
2. 構造テストでは検出できない“仕様漏れ”がある
- 構造テストは「コードに書かれたこと」をテストするため、書かれていない処理は検出できない。
- 決定表を基にすれば、あるべきなのに存在しない判断も検出可能。
3. テスト観点の明確化
- 決定表があれば、どの条件と結果をテストしているのかが明示的になる。
- テストの漏れ・重複が見つけやすくなり、関係者間のレビューもしやすくなる。
決定表と構造テストの関係
決定表をもとにテストケースを作成することで、結果的にコードの分岐(if文など)もカバーされることがあります。しかし、以下のようなケースでは構造テストだけでは不十分です:
- ロジックがポリモーフィズム(継承・動的ディスパッチ)で記述されている
- 条件が設定ファイルやデータベース値に依存している
これらはコード中の分岐として明示されないため、構造テストでは見逃される可能性があります。以下に具体例を示します。
ポリモーフィズムで記述されている場合
ポリモーフィズムとは、同じメソッド名でも、呼ばれる内容がクラスごとに違う仕組みです(オブジェクト指向の特徴です)。
// java
class User {
void showDiscount() {
System.out.println("No discount");
}
}
class PremiumUser extends User {
void showDiscount() {
System.out.println("20% discount");
}
}
// java
User u = getUser(); // 通常ユーザーかプレミアムユーザーかわからない
u.showDiscount(); // 実行時にどちらのクラスかで呼ばれる内容が変わる
このように分岐が if文などではなく「クラスの種類」で決まる場合、構造テスト(if文などコードの分岐をたどるテスト)では見逃してしまう可能性があります。
条件が設定ファイルやDBに依存している場合
アプリの動きがコード内の条件文ではなく、外部ファイルの値に左右されるケースです。
// config.json
{
"isFeatureEnabled": true
}
# python
if config["isFeatureEnabled"]:
enable_feature()
これは true
や false
の値がコードではなく外部ファイルにあるので、構造テストではこの if が true になるかをカバーしたつもりでも、実際の動作は設定ファイルの値次第です。
上記のようなケースでも、モデルベーステスト(決定表)であれば、仕様ベースで網羅的に確認することが可能です。言い換えれば、構造テストは「コードが意図どおりに動くか」を確認するものであり、ソフトウェアが本来達成すべき目的や意味には踏み込みません。
一方で、モデルベーステストは、仕様やユーザーの視点から「そもそも必要な判断や機能が備わっているか」を確認するためのアプローチです。コードだけでは見えない機能の漏れや設計の誤解を検出するのに非常に有効です。
構造テスト(Structural Testing)
観点:どの分岐が実行されたか?
- コードの構造(if文、ループ)に着目
- ソフトの目的や意味には踏み込まない
- 仕様書 + コードがあれば実施可能
モデルベーステスト(Model-Based Testing)
観点:本当に必要な判断ができているか?
- ドメイン知識やユーザー視点が必要
- 「何をどう判断すべきか?」をモデル(決定表など)で表現
- 非技術者や上流工程の関係者も参加しやすい
まとめ
決定表ベースのテストには、次のような強みと実務的メリットがあります。
- 非ブール値(例:容量=なし/0GB/8GB/無制限 など)を扱いやすい
- ドメイン専門家にも分かりやすく、レビュー・優先順位づけに役立つ
- 実装されていない仕様の漏れも検出できる
観点 | 構造テスト | モデルベーステスト(決定表) |
---|---|---|
目的 | コードの網羅性 | 仕様の正しさ |
必要なもの | コード、仕様書 | 要件、ドメイン知識 |
実施者 | 開発者、テスター | 上流工程の関係者(コンサル等) |
強み | 分岐の網羅性 | 仕様漏れの発見、非ブール条件の扱い |
決定表は上流から下流へとつながる“橋”のような存在です。仕様を明確にし、設計・実装を導き、さらに仕様通りに動作しているかを検証できます。構造テストと組み合わせることで、「正しく作る」と「作るべきものを作る」の両立が可能となり、より信頼性の高いソフトウェアが実現できます。つまり、仕様の抜けも、実装のミスも、どちらも見逃さないテスト戦略が高品質を担保するというロジックになっているのですね。
Discussion