- Pythonの修正後は「実行できた」だけで終わらず、入力と期待する結果をセットにして確認します。
- まず通常の入力、境界の入力、受け入れない入力を分け、小さな確認表を作ります。
- 同じ確認を繰り返すならunittestで残せます。テストが通っても、確認していない条件まで正しいとは言えません。
Pythonのエラーを直したけれど、別の入力でも正しく動くか自信がない初心者向けの記事です。短い関数を題材に、期待値を決めて、再実行できるテストを一つ作るところまで扱います。Webアプリ全体の動作、速度、安全性をこの記事だけで保証するものではありません。
テストは「入力に対する期待値」を確かめる
テストでは、実際の結果を、事前に決めた期待する結果と比較します。エラーが消えたか、画面に何か出たかだけでは、目的に合っているか分かりません。まず教材の仕様や、自分が決めた条件を読み直します。
期待値を実装の出力に合わせて書き換えると、間違いを見逃します。「コードがそう動くから正解」ではなく、「必要な条件がこうだから、この入力ではこの結果」と決めます。条件そのものが不明なら、先に質問して確定する方が近道です。
- 仕様:何を受け取り、何を返すか
- 確認表:入力と期待値を並べる
- 実行:実際の結果と比較する
- 保存:不一致を直し、同じ確認を残す
| 種類 | 確かめたい点 | 選び方 |
|---|---|---|
| 通常 | 想定する使い方で目的を満たすか | 教材の代表的な入力を一つ選ぶ |
| 境界 | 条件が切り替わる位置を間違えていないか | しきい値の直前・ちょうど・直後を試す |
| 受け入れない入力 | 仕様にない値をどう扱うか | 空欄、範囲外、別の型などを仕様に合わせて選ぶ |
どのコードでも全ての種類が同じように必要とは限りません。例えば文字をそのまま保存する処理では、空文字が有効なこともあります。「空欄は必ずエラー」と決めつけず、受け入れる条件を明確にします。
例:18を含む条件を、17・18・19で確認する
ここでは、整数の年齢を受け取り「18以上ならTrue、それ未満ならFalse」を返す学習用の関数を作ります。入力は0以上の整数とし、それ以外はValueErrorにします。実際のサービスの利用条件ではありません。
def is_eligible(age):
if type(age) is not int or age < 0:
raise ValueError("age must be a non-negative integer")
return age >= 18
type(age) is not intを使うのは、この例で真偽値を整数の入力として受け入れない仕様にしたためです。Pythonではboolはintのサブクラスなので、isinstance(True, int)はTrueです。この違いまで含め、目的に合う入力条件を選んでいます。
| 入力 | 期待する結果 | 確認の理由 |
|---|---|---|
| 17 | False | 境界の直前 |
| 18 | True | 境界を含むこと |
| 19 | True | 境界の直後 |
| 0 | False | 入力として受け入れる下限 |
| -1、”18″、True | ValueError | この例で受け入れない入力 |
まずはprint(is_eligible(18))などで値を見ても構いません。関数の内部で何が起きたかを調べるなら、printで値と型を確認する方法へ進めます。ただし、表示を目で比べるだけだと、確認漏れや見間違いが起こり得ます。同じ条件を繰り返すなら、次のように比較をコードへ残します。
unittestなら、入力と期待値を繰り返し確認できる
unittestはPython標準ライブラリのテスト用の仕組みです。次のコードを、先ほどの関数の下へ追加し、例えばtest_eligible.pyとして保存します。外部パッケージの追加は不要です。
import unittest
class EligibilityTests(unittest.TestCase):
def test_boundary(self):
for age, expected in [(17, False), (18, True),
(19, True), (0, False)]:
with self.subTest(age=age):
self.assertEqual(is_eligible(age), expected)
def test_invalid_input(self):
for age in [-1, "18", True]:
with self.subTest(age=age):
with self.assertRaises(ValueError):
is_eligible(age)
if __name__ == "__main__":
unittest.main()
自分の環境でPythonを起動するコマンドに続けて、このファイルを指定します。例えばpython3 test_eligible.pyです。教材や環境によってはpythonやpyを使うので、普段動いているコマンドに合わせてください。
この例では2つのテストメソッドが実行されます。成功すると、末尾にOKが表示されます。assertEqualは実際と期待の値の比較、assertRaisesは指定した例外が発生するかの確認です。subTestは、どの入力で不一致が起きたかを追いやすくします。
失敗が出たら、実装と期待値の両方を仕様へ戻す
関数の最後をreturn age > 18に変えると、18の入力で期待値Trueと実際Falseが一致せず、テストが失敗します。そのときは表示されたテスト名と入力を読み、条件が「18以上」なのか「18より大きい」なのかを再確認します。
今回は仕様が18以上なので、実装を>=に戻す修正が必要です。失敗表示を消すためだけに期待値をFalseへ変えてはいけません。反対に、仕様を誤解して期待値を書いていたなら、期待値側を直すこともあります。
テストを残すメリットと、保証できないこと
メリットは修正のたびに同じ確認を繰り返せることです。以前直した条件を壊していないかにも気づきやすくなります。一方、テストを書く時間と、仕様変更に合わせて更新する手間が必要です。期待値を間違えたり、大切な入力を選び忘れたりすれば、OKでも不具合は残ります。
この例では外部サービス、画面操作、ファイル保存、性能、安全性は確認していません。複数の部品がつながるアプリでは、部品のテストに加えて、利用者の操作に沿った確認も必要です。勤務先の本番データを使って、無断で破壊的な処理を試す方法には向きません。学習用の独立した環境と架空データを使ってください。
通常のassert文との違いにも注意
assert文は短い確認に使えますが、最適化オプション(-O)では実行されません。利用者の入力を必ず拒否する処理をassertだけに任せないでください。上の例は入力条件をifとraiseで確認し、テスト側ではunittestのメソッドで比較しています。
まとめ:今日直した条件を、次も試せる形にする
- 入力と期待値を先に決め、通常・境界・受け入れない入力を分ける。
- 不一致なら、実装も期待値も仕様と照合する。
- 修正後は、失敗した入力だけでなく以前の確認も再実行する。
- OKと未確認を区別し、確認範囲を記録する。
対象の関数・ファイル:[ ]
仕様と入力条件:[ ]
入力/期待値/実際:[ ]
変更箇所と再実行結果:[ ]
未確認の条件:[ ]
まず一つの関数について、代表的な入力と境界の入力を選び、期待値を書いてください。仕様が分からなければ質問テンプレートへ整理し、確認できた範囲は学習記録に残します。
技術情報の最終確認:2026年10月10日。Python公式のunittest、assert文を参照。条件表・コード・判断図は学習用の編集部作成例です。