目で見ていた確認を何度でも実行できる形にする
関数を修正するたびにprintの出力を目で比較しているなら、pytestで期待値と期待する例外を記録すると、同じ確認を繰り返せる。まず正常な入力で値が合うこと、次に不正な入力で決めた例外になることをテストする。例外が出ないことだけを確認しても、間違った値を静かに返す不具合は見つからない。
以下の3ファイルを同じフォルダーへ保存する。計算する関数とテストを分け、通常の成功するテストと、失敗表示を読むための意図的な誤ったテストも別ファイルにする。後者を含めて全体が成功したとは扱わない。掲載例はpytest 9.1.1で実際に実行している。pytestを導入する場合は、自分の作業用仮想環境へ入れて実行する。
正常値とValueErrorをテストする
stats_tools.py
def mean(values):
if not values:
raise ValueError("values must not be empty")
return sum(values) / len(values)
test_stats.py
import pytest
from stats_tools import mean
def test_mean_normal():
assert mean([1, 3]) == 2.0
def test_mean_empty():
with pytest.raises(ValueError, match="must not be empty"):
mean([])
このフォルダーでpython -m pytest -q test_stats.pyを実行する。以下は実行結果の集計部分の抜粋で、経過時間の表示は省略したもの。2件とも成功し、終了コードは0だった。
実行結果(抜粋)
2 passed
意図的に失敗するテストから表示を読む
test_wrong.py
from stats_tools import mean
def test_wrong_expected_value():
assert mean([1, 3]) == 3.0
こちらは期待値をわざと3.0にした誤ったテストである。python -m pytest -q test_wrong.py –tb=shortとして実行すると1件失敗し、終了コードは1になる。次は比較内容の抜粋である。関数の計算結果2.0と、テストに書いた期待値3.0が一致していない。
実行結果(抜粋)
E assert 2.0 == 3.0
E + where 2.0 = mean([1, 3])
assertとraisesの役割
pytestはtest_で始まる関数を見つけて実行し、assertの比較に失敗するとその内容を表示する。どのファイルのどのテストが失敗したかを先に見て、期待値と実際の値を比較する。失敗したからといって、すぐ期待値を実際の値へ書き換えるのではなく、仕様に照らして関数とテストのどちらが間違っているか判断する。
pytest.raises()のwithの中では、指定した例外が起きることが期待される。例外が出なければ失敗し、別の種類ならその問題が分かる。matchは例外メッセージに対する正規表現なので、特殊記号を含む文字列をそのまま書くと意味が変わる場合がある。例では分かりやすい固定の語句で確認している。
raisesの範囲は、例外が出るはずの一つの操作へ絞る。準備処理までwithへ入れると、準備の失敗を目的の例外として誤って受け入れることがある。今回のmean([])は空入力を明示的に拒否する契約なので、その関数呼び出しだけを囲んでいる。
実行するテストを明示する
python -m pytestを使うと、そのpythonに対応したpytestで実行できる。別の仮想環境のpytestコマンドを誤って呼ぶ問題を調べる手がかりにもなる。ファイルを指定せず実行するとtest_wrong.pyも収集されるため、この教材の全テストは意図どおり1件失敗する。普段の回帰テストへ残すのは正しい期待値に直したテストである。
assertは通常のPythonでは最適化オプションで省略されうるため、利用者入力の必須検証をassertだけで書かない。例のmean()はifとraiseで空入力を確認し、テスト側でassertを使っている。実行時の入力検証と開発時の自動確認を分けると、役割がはっきりする。
小数を扱う関数では、浮動小数点の丸めを考えてpytest.approx()などを使う場面もある。どんな結果にも適当な許容誤差を付けるのではなく、値の意味と必要な精度から決める。ここでは正確に表現できる2.0を使い、テストの始め方に集中している。
最初のテストを増やすとき
実際に見つかった不具合は、再現する最小入力を追加してから修正すると、直ったかを確かめやすい。正常例だけを増やすより、空、1件、境界値、期待する例外をそろえると取りこぼしを減らせる。外部サービスや実データに依存する部分は、小さな関数へ分けたりテスト専用の入力を用意したりして、毎回同じ条件で確認できるようにする。
実行環境と関連情報
掲載コードはLinux上のCPython 3.12.14で実行した。pytestを使う例はpytest 9.1.1で確認している。OS固有のファイル操作や対話環境の違いは、本文に記した条件に従って扱う。
関連:pytestのparametrizeで境界値と入力パターンをまとめて検証する / fixtureとtmp_pathで実データを壊さずファイル処理をテストする / doctestで説明文のコード例が古くなるのを防ぐ
- pytest公式:アサーション(2026年10月2日参照)
