12 / 90

Day 12: テストと再現可能なプログラム

観察できる振る舞いを検査するテストのアサーションを書き、一時ディレクトリに使い捨て fixture を作り、失敗したテストをコードのバグ・テストのバグ・環境の故障にトリアージしてから編集する。 Retrieval and lower-new-content review are part of the 480-minute ledger.

Self-Directed Learning & Python 480 active minutes (8 hours focused study)
01 120 分

振る舞いを記録するテストのアサーション

予測と観察した証拠を記録した後にのみ支援を許可。

Prerequisites

前のアクティビティを完了していること。行き詰まったら、記載されている回復リンクを使う。

Learning Objective

assertEqual と assertRaises を使って unittest.TestCase を書き、python -m unittest -v を実行し、最後の OK の行が何を証明し何を証明しないかを説明する。

Instructions

各 test_ メソッドは1つの振る舞いの主張で、名前は test_empty_list_returns_zero のようにドキュメントとして読めるべき。assertEqual(actual, expected) は値を検査し、with self.assertRaises(例外型): は呼び出しが契約どおりに失敗することを証明する。テストの実行方法は2つある。プロジェクトディレクトリでの python -m unittest -v(python -m unittest discover -v と同じ)は、そのディレクトリの test_*.py という名前のモジュールを発見する。python test_counter.py は、ファイル末尾の unittest.main() を通じてそのファイルに定義されたテストだけを実行する。unittest.main() は隣にある test_*.py モジュールを発見しない。

Practice & Prediction Log

空のディレクトリに counter.py を作り、空白以外の文字を含む要素の個数を返し、要素が str でなければ TypeError を投げる count_non_empty(lines) を書く。test_counter.py には4つのテストを持つ CounterTests クラスを書く:空リストは0を返す、空白だけの行は無視される、混在する行では空でない行だけ数える、そして str でない要素(例えば None)は TypeError を投げる(assertRaises で検査)。4つの結果を予測してから、そのディレクトリで python -m unittest -v を実行する。Python 3.13/3.14 での期待される実行(テストはアルファベット順に実行され、時間は変動する。古い Python では括弧内の名前が短く表示されることがある):
test_empty_list_returns_zero (test_counter.CounterTests.test_empty_list_returns_zero) ... ok
test_mixed_lines_count_only_non_empty (test_counter.CounterTests.test_mixed_lines_count_only_non_empty) ... ok
test_non_string_item_raises_type_error (test_counter.CounterTests.test_non_string_item_raises_type_error) ... ok
test_whitespace_only_line_is_ignored (test_counter.CounterTests.test_whitespace_only_line_is_ignored) ... ok
...
Ran 4 tests in 0.001s
OK

Independent Mastery Criterion

実行が assertRaises のテスト1つを含めて Ran 4 tests ... OK で終わり、OK が「これらの入力ですべてのアサーションが成立した」意味であり「関数がすべての可能な入力で正しい」意味ではないと述べられる。

Authoritative Source Resources & Provenance

linking PSF License Version 2

Python Standard Library: unittest

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: Classes and functions: Test cases (TestCase assert methods)

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Normative reference for unittest.TestCase, assertEqual, assertRaises, and python -m unittest output.

ソース前提知識:

安全フォールバック(オフライン回復性):

unittest ships with the interpreter, so every example runs offline; help(unittest.TestCase) documents the assert methods.

比較対象候補リソースと選定評価 (2)
pytest documentation ↗ compared_alternative [MIT]

Strong third-party runner, but it adds an installation step and its own conventions; unittest needs no environment change and is the baseline the rest of the curriculum assumes.

doctest — Test interactive Python examples ↗ compared_alternative [PSF License Version 2]

Docstring-embedded examples suit tiny pure functions; unittest was selected for the structured TestCase isolation the check suites need.

02 120 分

一時 fixture でファイル処理をテストする

予測と観察した証拠を記録した後にのみ支援を許可。

Prerequisites

前のアクティビティを完了していること。行き詰まったら、記載されている回復リンクを使う。

Learning Objective

tempfile.TemporaryDirectory と Path.write_text でテスト内にファイル fixture を作り、summarize_scores の3つの振る舞い——有効な内容、ファイル不在、不正な内容——をカバーする。

Instructions

tempfile.TemporaryDirectory() は自動で削除されるプライベートなディレクトリを作るので、テストは本物のユーザーファイルに触れない。テストが依存するパスは、存在してはならないパスも含めてすべてそのディレクトリの中に作る:Path(d) / "does-not-exist.txt" は何とも衝突しないが、裸の "does-not-exist.txt" は現在の作業ディレクトリを基準に解決されるので、そこに同名のファイルがあると test_missing_file が失敗する。このアクティビティは厳格な契約を使う:summarize_scores(path) は {name: 最高点} を返し、空行は無視し、ファイル不在では FileNotFoundError、不正なスコア行では ValueError を投げる。代わりに Day 10 の寛容な契約をテストする場合は missing フラグと skipped リストをアサートする——契約を1つ選んで一貫してテストする。

Practice & Prediction Log

次のスイート(同じディレクトリの test_scores.py)が通るように scores.py を書く:
# file: test_scores.py
import tempfile
import unittest
from pathlib import Path

from scores import summarize_scores


class SummarizeTests(unittest.TestCase):
def test_valid_file(self):
with tempfile.TemporaryDirectory() as d:
p = Path(d) / "scores.txt"
p.write_text("ana,7\nbo,9\nana,8\n", encoding="utf-8")
self.assertEqual(summarize_scores(str(p)), {"ana": 8, "bo": 9})

def test_missing_file(self):
with tempfile.TemporaryDirectory() as d:
missing = Path(d) / "does-not-exist.txt"
with self.assertRaises(FileNotFoundError):
summarize_scores(str(missing))

def test_invalid_score(self):
with tempfile.TemporaryDirectory() as d:
p = Path(d) / "scores.txt"
p.write_text("ana,7\nbo,x9\n", encoding="utf-8")
with self.assertRaises(ValueError):
summarize_scores(str(p))
# end file
予測してから python -m unittest -v を実行する。期待:Ran 3 tests ... OK。次にそのディレクトリに does-not-exist.txt という名前のファイルを作ってもう一度実行する:不在のパスは専用の新しい一時ディレクトリの中にあるので、結果は変わらず Ran 3 tests ... OK でなければならない。

Independent Mastery Criterion

スイートが自己完結していてクリーンな実行で通り、fixture が使い捨てで、各期待値が暗示ではなくテスト名かアサーションに見えている。

Authoritative Source Resources & Provenance

linking PSF License Version 2

Python Standard Library: unittest

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: Classes and functions: Test cases (TestCase assert methods)

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Normative reference for the three-behavior fixture test suite.

ソース前提知識:

安全フォールバック(オフライン回復性):

The fixture suite runs entirely on the local interpreter and temporary directories.

比較対象候補リソースと選定評価 (2)
pytest documentation ↗ compared_alternative [MIT]

pytest tmp_path fixtures are elegant, but tempfile plus unittest keeps the zero-dependency guarantee for this block.

doctest — Test interactive Python examples ↗ compared_alternative [PSF License Version 2]

Docstring-embedded examples suit tiny pure functions; unittest was selected for the structured TestCase isolation the check suites need.

linking PSF License Version 2

Python Standard Library: tempfile

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: TemporaryDirectory for disposable on-disk fixtures

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Normative reference for creating and auto-removing fixture files inside tests.

ソース前提知識:

安全フォールバック(オフライン回復性):

TemporaryDirectory is part of the installed standard library and needs no page access.

比較対象候補リソースと選定評価 (2)
pytest tmp_path fixture ↗ compared_alternative [MIT]

Equivalent capability scoped to the pytest runner; deferred until the curriculum adopts pytest.

io — Core tools for working with streams ↗ compared_alternative [PSF License Version 2]

Lower-level file-object interface without automatic cleanup semantics; tempfile was selected for the safe scratch-file pattern the drills require.

03 120 分

編集前に失敗したテストを診断する

予測と観察した証拠を記録した後にのみ支援を許可。

Prerequisites

前のアクティビティを完了していること。行き詰まったら、記載されている回復リンクを使う。

Learning Objective

total_score と、AssertionError: 20 != 30 を報告する1つの失敗テストが与えられたとき、何かを編集する前に、コード・テスト・環境のどれが間違っているかを判定する。

Instructions

FAIL ブロックはテスト名、失敗したアサーション行、観察値と期待値を示す。例外クラスは手がかりであって判決ではない。ModuleNotFoundError/ImportError や FileNotFoundError なら、環境——どのインタプリタが動いているか、どのパッケージが入っているか、作業ディレクトリ、パス——を最初に強く疑うが、責める前に sys.executable、Path.cwd()、Path(...).exists() などの証拠で確かめる。AssertionError はコードが実行され、テストが予期しない値を出したという意味である。たいていはコードかテストを指すが、設定・データファイル・環境の違いでも結果は変わりうるので、例外の型だけで環境を除外することは決してできない。ここでの証拠は:テストが自分の3行 fixture を TemporaryDirectory 内に書き込む(外部ファイルも cwd 依存も無い)、試したどのインタプリタでも同じ失敗が再現する、そして観察20と期待30の差はちょうど10点の行1つ分である。total_score は fixture に存在しないヘッダ行を読み飛ばそうと f.readlines()[1:] を呼び、最初の本物の行を落としている。テストの期待が正しい——ファイルにヘッダは無い——ので、最小の修正はスライスを消すことで、テストを編集することではない。

Practice & Prediction Log

次の2つのファイルを空のディレクトリに置く:
# file: total.py
def total_score(path):
"""Return the sum of the scores in rows like "a,10" (no header row)."""
total = 0
with open(path, encoding="utf-8") as f:
lines = f.readlines()[1:]
for line in lines:
name, _, raw = line.strip().rpartition(",")
total += int(raw)
return total
# end file
# file: test_total.py
import tempfile
import unittest
from pathlib import Path

from total import total_score


class TotalTests(unittest.TestCase):
def test_three_rows(self):
with tempfile.TemporaryDirectory() as d:
p = Path(d) / "rows.txt"
p.write_text("a,10\nb,10\nc,10\n", encoding="utf-8")
self.assertEqual(total_score(str(p)), 30)
# end file
結果を予測してから python -m unittest -v を実行する。FAIL: test_three_rows ... AssertionError: 20 != 30 が報告される。編集する前に診断を記録する:症状(1行落とされた)、証拠(fixture はテスト内で作られる。30 - 20 = 10点の行1つ)、仮説([1:] スライスがヘッダを仮定している)、分類(コードのバグ、テストが権威)、最小の修正(全行を読む)、そして再実行して OK を確認。

Independent Mastery Criterion

証拠をもって失敗をコードのバグと正しく分類し、テストの期待が成立する根拠を説明し、最小の修正後の再実行が OK を報告する。

Authoritative Source Resources & Provenance

linking PSF License Version 2

Python Standard Library: unittest

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: Classes and functions: Test cases (TestCase assert methods)

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Normative reference for reading unittest failure reports during the code-vs-test-vs-environment triage.

ソース前提知識:

安全フォールバック(オフライン回復性):

The seeded bug and its failure output reproduce locally with the installed interpreter.

比較対象候補リソースと選定評価 (2)
pytest documentation ↗ compared_alternative [MIT]

pytest produces richer diffs, but the triage skill is taught on the standard runner every learner already has.

doctest — Test interactive Python examples ↗ compared_alternative [PSF License Version 2]

Docstring-embedded examples suit tiny pure functions; unittest was selected for the structured TestCase isolation the check suites need.

04 120 分

再現可能プログラムのキャップストーン

独立習得チェック(AI の支援なし)。

Prerequisites

前のアクティビティを完了していること。行き詰まったら、記載されている回復リンクを使う。

Learning Objective

新しい一時ディレクトリで再現可能なプログラムを組み立てる:summarize_scores を実装した scores.py、3つの fixture をカバーする test_scores.py、そして requirements.txt。その後 python -m unittest -v を実行し、全テストが通ることを示す。

Instructions

資料を見ずに(クローズドブックで)行う。requirements.txt は依存関係を記録する。プログラムが標準ライブラリだけを使うなら、# stdlib only, no third-party dependencies のようなコメント行で十分に有効な requirements ファイルになる。選んだインタプリタでスイートを実行し、それを記録する。テストが1つも実行されなかった(Ran 0 tests)と報告されたら、test_*.py の命名規約と発見を実行したディレクトリを確認する。

Practice & Prediction Log

期待される結果:scores.py、test_scores.py、requirements.txt だけがある新しいディレクトリで、python -m unittest -v が Ran 3 tests ... OK(有効なファイル、ファイル不在、不正なスコア)で終わる。コマンドライン python scores.py FILE は成功時に要約の辞書を表示して 0 で終了する。ファイル不在や不正な行では、トレースバックを出す代わりに stderr に1行のエラーを出して 1 で終了する。したがってトレースバックで終わるのは予期しないエラーだけである。ログには予測・観察した出力・使ったインタプリタを記録する。

Independent Mastery Criterion

独力で、クリーンなディレクトリ実行で3つのテストすべてが OK、requirements.txt が依存関係を正直に列挙、書面の回復計画がスイート失敗時の行き先を名指し、の3点を満たす。

Authoritative Source Resources & Provenance

linking PSF License Version 2

Python Standard Library: unittest

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: Classes and functions: Test cases (TestCase assert methods)

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Normative reference for the discovery convention the capstone depends on.

ソース前提知識:

安全フォールバック(オフライン回復性):

The capstone is a clean-directory local run with no online dependency.

比較対象候補リソースと選定評価 (2)
pytest documentation ↗ compared_alternative [MIT]

Kept as a future alternative runner; the capstone must pass with zero third-party installs.

doctest — Test interactive Python examples ↗ compared_alternative [PSF License Version 2]

Docstring-embedded examples suit tiny pure functions; unittest was selected for the structured TestCase isolation the check suites need.

linking PSF License Version 2

The Python Tutorial: Virtual Environments and Packages

著者・提供機関: Python Software Foundation | オリジナル出所URL ↗

該当セクション・タイムスタンプ: Section 12 (requirements files and reproducible environments)

検証バージョンと日付: Python 3.12 documentation; verified 2026-10-30

選定理由:

Reference for the requirements.txt the capstone assembles to make the program repeatable.

ソース前提知識:

安全フォールバック(オフライン回復性):

The capstone requirements file only needs the standard library, so no install is required.

比較対象候補リソースと選定評価 (2)
pip documentation ↗ compared_alternative [MIT]

Reference for requirements syntax when the learner later adds real third-party dependencies.

virtualenv documentation ↗ compared_alternative [MIT License]

Mature third-party environment tool with overlapping behavior; the stdlib venv tutorial was selected to avoid an extra dependency for first-time setup.