集計コードを直した後は、合計が一度出たことより、決めた入力条件で同じ結果になるかを確かめます。正常な値だけでなく、0、空の入力、数値でない文字なども小さな例として残せます。
標準ライブラリのunittestを使うと、その確認を繰り返せる形にできます。期待する結果は実装の式から作らず、入力と業務の条件から決めることがポイントです。
集計する部分を小さな関数にする
次は整数の合計と平均だけを返す架空の関数です。CSVの読み込みとは分けています。空の入力や整数でない値は、勝手に0へ直さずValueErrorにする条件です。
import unittest
def summarize(values):
if not values:
raise ValueError("データがありません")
if any(type(value) is not int for value in values):
raise ValueError("整数でない値があります")
total = sum(values)
return {"total": total, "mean": total / len(values)}
class SummaryTests(unittest.TestCase):
def test_normal(self):
self.assertEqual(summarize([100, 200, 0]), {"total": 300, "mean": 100})
def test_zero(self):
self.assertEqual(summarize([0]), {"total": 0, "mean": 0})
def test_negative(self):
self.assertEqual(summarize([-10, 5]), {"total": -5, "mean": -2.5})
def test_empty(self):
with self.assertRaises(ValueError):
summarize([])
def test_invalid_type(self):
for value in ["10", None, True]:
with self.subTest(value=value):
with self.assertRaises(ValueError):
summarize([value])
if __name__ == "__main__":
unittest.main()
Windows・Python 3.12.10で実行し、五つのテストメソッドが成功しました。文字列、None、Trueの違いは、最後のメソッドのsubTestで分けています。端末へ出る試験結果は通常stderrで、合計値を標準出力へ印字するプログラムではありません。
0と空入力を同じにしない
一件の0は、有効な値が一件あって合計も平均も0です。空入力は平均の分母となる件数がありません。この例では二つを別の条件として扱っています。
入力が空のときに0を返す業務もあり得ますが、その場合は期待値を先に決め直します。エラーを出さないようにするためだけに既定値を足すと、入力が届かなかった状態を正常な0と誤解することがあります。
負数を認めるかどうかも業務次第です。この例は負数を含む整数を認めています。請求や在庫などへ使うなら、その列で負数が何を表すかを確認してください。
失敗したケースの理由を読む
テストが失敗した場合は、実装の不具合だけでなく、入力条件や期待値を変更すべき理由があるかも見ます。結果に合わせて期待値だけを書き換えると、何を守る試験か分からなくなります。
assertRaisesは指定した例外が起きるかを調べます。何か例外が出ればよい、という広い判定にすると、別の理由で止まる不具合も見逃す可能性があります。必要なら例外の内容も確認します。
CSVの読み込みを別に試す
関数の試験が通っても、実ファイルの文字コード、列名、カンマ、空欄が正しく読めることまでは確認していません。読み込み部分にはその条件を持つ小さなCSVを用意します。
最初は今回のような少数の固定入力を残し、修正で条件が増えたときに、その条件を示すケースを足します。件数だけ増やすより、どの失敗を見つけるための例かが説明できる試験にします。
集計の検算はCSV集計の基本、例外の区別はPythonの例外処理で補えます。
参考資料
Python unittest公式資料を2026年11月1日に確認しました。
