到達点は、正常系だけでなく境界と失敗を再現するテストを書くことです。前提は第06・10回です。
期待結果は仕様から選ぶ
テストは実装をなぞるためでなく、利用者との契約を守るために書きます。「5000円以上なら送料無料」なら、4999と5000が重要です。実装と同じ式で期待値を計算すると、同じ誤りを見逃す場合があります。
次をtest_fees.pyに保存します。
import unittest
def shipping(amount: int) -> int:
if amount < 0:
raise ValueError("negative amount")
return 0 if amount >= 5000 else 500
class ShippingTest(unittest.TestCase):
def test_boundary(self):
for amount, expected in [(0, 500), (4999, 500), (5000, 0)]:
with self.subTest(amount=amount):
self.assertEqual(shipping(amount), expected)
def test_negative_is_rejected(self):
with self.assertRaises(ValueError):
shipping(-1)
if __name__ == "__main__":
unittest.main()
python -m unittest -v test_fees を実行すると二つのテストが成功します。subTestを使うと、どの入力で失敗したかが分かります。テスト対象は準備、操作、検証の順で読み取れるようにします。
失敗したテストから小さく調べる
条件をわざと amount > 5000 に変え、5000のケースで失敗することを確認してから元に戻します。テストが本当に変更を検知するかを見る方法です。
デバッグではtracebackの最後の例外名と、自分のコードの該当行を確認します。必要なら breakpoint() で止め、pdbから変数や呼び出し元を調べます。あちこちを書き換える前に、最小の失敗入力を残してください。
単体テストは小さな判断、統合テストはファイルやDBとの接続、CLIテストは終了コードや出力を確かめます。外部サービスを毎回呼ぶテストは不安定になりやすいため、差し替え可能な境界やローカルサーバーを使います。時間に依存する処理には基準時刻を渡します。
練習と解答
練習:空の保存ファイルと壊れた保存ファイルは、同じ期待結果にすべきでしょうか。
解答:保存形式次第です。空のJSONファイルは有効な空配列ではないので、普通は破損として区別します。「読めなければ空を返す」実装をテストが追認しないよう、仕様から期待結果を決めます。
公式資料
Python全20回の目次 | 前の回 | 次の回