境界値テストで実際の不具合を見つける:設計の考え方
一言でいうと
良い境界値テストは、もっともらしく間違った実装と、正しい実装を区別します。
なぜ必要なのか
10行の料金計算機でも、境界が1つ間違っていれば、すべての注文に誤った金額を請求します。 price(3)が1050であることを確認するテストは、割引のしきい値の>=が>に変わっても成功します。 多くの中間値を繰り返すより、条件が変わる直前・その地点・直後を選ぶ必要があります。 このラボでは、カバレッジレポートの数字を読みません。作成したテストを正常なコードで 実行したあと、条件が1つ間違っているコードで再実行し、違いを実際に観察します。
どう動くのか
| 入力 | 契約 |
|---|---|
| 整数0 | 0ウォン |
| 整数1–9 | 1個あたり350ウォン |
| 整数10–100 | 全数量に1個あたり300ウォン |
| 範囲外の整数 | ValueError |
| 整数以外の値・bool | TypeError |
割引は、10個目以降だけに適用する累進方式ではなく、全数量の単価を変えます。 この一文がないままテストを書くと、異なる方針を前にして、どの実装が正しいのかを議論することになります。 テスト名と期待する金額には、方針の意味が表れている必要があります。
現場での姿
Pythonでは、boolがintのサブクラスなので、isinstance(True, int)は真です。業務で 数量Trueを1個として受け付けてよいかどうかは、言語が決めてくれません。この契約は明示的に 拒否するので、boolを別の反例として用意します。一方で、実装の変数名やif文の数を検査すると、 同じ動作のリファクタリングをバグと誤認します。入力・結果・例外を観察する必要があります。
次のラボですること
0、1、割引の直前・境界・直後、最大値、負の数、誤った型の8つのテストを積み上げます。 正常な実装で失敗したら、まずテストの期待値を直します。欠陥のある実装で失敗しても、 文法エラーやインポートエラーは欠陥の検出として認められません。実行されたテストのassertionが 失敗するか、予想に反して実行中に例外が発生する必要があります。この欠陥サンプルをすべて検出できることは、検証した契約に対する証拠であり、すべてのバグが ないことの証明ではありません。
参考: pytestを始める