Pythonコードで、仕様書からテストコードの実装を行います。
また、同じ内容について、Python標準ライブラリunittest
と、テストフレームワークpytest
の2つを記述します。
src/bank
に実装された銀行口座の仕様(本READMEのセクション5)を読み解き、tests
ディレクトリにあなた自身のテストコードを記述します。
演習は、まずunittest
で実装し、次にpytest
で書き直す(リファクタリングする)ことで、両者の違いとpytest
の利便性を体感できるように構成されています。
行き詰まった場合は、tests_answer
ディレクトリに解答例が用意されていますので、参考にしてください。
本プロジェクトは、高速なPythonパッケージインストーラであるuv
を使用します。
-
uv
のインストールuv
が未インストールの場合、公式の指示に従いインストールしてください。 (例:pip install uv
またはpipx install uv
) -
リポジトリのクローンと移動
git clone https://github.com/hikaruy0804/learn_python_testcode.git cd learn_python_testcode
-
仮想環境の作成
uv venv
プロジェクトルートに
.venv
という仮想環境が作成されます。 -
依存パッケージのインストール
uv sync
pyproject.toml
とuv.lock
を基に必要なライブラリ(pytest
など)が仮想環境にインストールされます。
この演習は2つのステップで進め、unittest
とpytest
の違いを体感することを目指します。
まずは、Pythonの標準ライブラリであるunittest
を使い、仕様を満たすテストをtests/test_bank_unittest.py
に記述してみましょう。
unittest
の基本:
unittest.TestCase
を継承したクラスを作成test_
で始まるメソッドを定義self.assertEqual()
などでアサーションwith self.assertRaises()
で例外を検証
テストの実行:
uv run python -m unittest discover -s tests -p "test_*.py" -v
実行結果の確認:
実行後、コンソールの最終行にOK
と表示されれば、すべてのテストが正常に完了しています。
tests/test_bank_pytest.py
にpytest
を使って同じテストを記述してみましょう。
pytest
の実行:
uv run pytest -v tests
実行結果の確認:
実行後、... passed in ...
のような緑色のメッセージが表示されれば成功です。
pytest
の利点:
- 記述量の削減: クラス定義が不要になり、コードが簡潔になります。
assert
文: Python標準のassert
だけで、より詳細な失敗レポートが得られます。- Fixture:
@pytest.fixture
を使うことで、テストの準備・後片付け処理を柔軟に共通化できます。 - Parametrize:
@pytest.mark.parametrize
を使うと、複数の入力パターンを持つテストを簡単に自動生成できます。
- 単純な銀行口座ドメインを実装する。入出金・振替を提供。
- 金額は整数(最小単位:円)で扱い、小数は不可。
- 例外と境界条件を明確に定義する(テストしやすい仕様)。
- ユーザーは口座を作成し、入金・出金できる。
- 2つの口座間で振替できる。
- 口座残高は常に 0 以上でなければならない。
- クラス:
Account(owner: str, balance: int = 0)
deposit(amount: int) -> None
withdraw(amount: int) -> None
- 関数:
transfer(src: Account, dst: Account, amount: int) -> None
- 例外:
OverdraftError
: 残高不足ValueError
: 不正金額・同一口座振替などの仕様違反TypeError
: 引数の型が不正
deposit
: 金額amount
はint
かつ> 0
。balance
に加算。withdraw
: 金額amount
はint
かつ> 0
。残高が足りるときのみ減算。transfer
:amount
はint
かつ> 0
src is not dst
src.withdraw(amount)
が成功した場合のみdst.deposit(amount)
を行う(部分更新なし)。
deposit
:amount <= 0
→ValueError
amount
がint
以外 →TypeError
withdraw
:amount <= 0
→ValueError
amount
がint
以外 →TypeError
amount > balance
→OverdraftError
transfer
:amount <= 0
→ValueError
amount
がint
以外 →TypeError
src is dst
→ValueError
src
残高不足 →OverdraftError
(この場合dst
は未更新)
- Python 3.10+ / 外部依存なし
- スレッドセーフ要件は持たない(学習用途のため単一スレッド想定)
- 例外メッセージは簡潔で一貫