assert って何?まずは一言でいうと
assert は、
「この条件が成り立っているはずだ」とコードに書いておくための仕組みです。
成り立っていれば何も起きない。
成り立っていなければ AssertionError を起こして、
「ここで想定外のことが起きた」と教えてくれます。
テストコードでは「期待する結果」を書く場所として、
アプリコードでは「ここは絶対こうなっている前提」を明示するために使います。
Python の assert の基本的な動き
いちばんシンプルな例
def add(a: int, b: int) -> int:
return a + b
result = add(1, 2)
assert result == 3
Pythonここで起きていることはこうです。
result == 3 を評価する
True なら何も起きない(そのまま次の行へ)
False なら AssertionError が発生してプログラムが止まる
つまり、assert は
「この条件が False になったら、そこで止まってくれ」
という“安全装置”です。
メッセージを付ける
assert にはメッセージも付けられます。
assert result == 3, "add(1, 2) は 3 になるはず"
PythonFalse になったときは、
AssertionError: add(1, 2) は 3 になるはず
のように表示されます。
「何を期待していたのか」をメッセージで残しておくと、
あとで自分や他人が読んだときに理解しやすくなります。
テストコードでの assert(pytest とセットで考える)
「期待する結果」を書くのが assert の役割
pytest では、assert がテストの主役です。
# calc.py
def mul(a: int, b: int) -> int:
return a * b
Python# test_calc.py
from calc import mul
def test_mul_simple():
assert mul(2, 3) == 6
def test_mul_zero():
assert mul(0, 5) == 0
Pythonここでの assert は、
mul(2, 3) の結果は 6 であるべきmul(0, 5) の結果は 0 であるべき
という「仕様」をコードで表現しています。
pytest は、assert が失敗したときに、
実際の値
期待した値
をきれいに表示してくれます。
> assert mul(2, 3) == 7
E assert 6 == 7
E + where 6 = mul(2, 3)
この「差分を見せてくれる」のが、pytest × assert の強力なところです。
例外が起きることも assert でチェックする(pytest.raises)
例外のテストは pytest.raises を使いますが、
本質的には「例外が起きる」という条件を assert しているのと同じです。
import pytest
def div(a: int, b: int) -> float:
if b == 0:
raise ValueError("b must not be zero")
return a / b
def test_div_zero():
with pytest.raises(ValueError) as excinfo:
div(1, 0)
assert "b must not be zero" in str(excinfo.value)
Pythonここでは、
div(1, 0) が ValueError を投げること
そのメッセージに特定の文字列が含まれること
を assert で確認しています。
アプリコードでの assert(「前提条件」を明示する)
「ここは絶対こうなっているはず」をコードに残す
テストだけでなく、アプリ本体のコードでも assert は使えます。
例えば、DB からユーザーを取ってくる関数があって、
「この時点では必ずユーザーが存在しているはず」という前提があるとします。
def get_user_name(user_id: int) -> str:
user = find_user_by_id(user_id)
assert user is not None, f"user_id={user_id} のユーザーが存在する前提"
return user.name
Pythonここでの assert は、
「もし user が None だったら、それはバグだからすぐ気づきたい」
という意図をコードに埋め込んでいます。
このように、assert は
「ありえないはずの状態になったら、静かに進まずに爆発してほしい」
という場所に置くと効果的です。
バリデーションとの違い
assert は「開発者の前提チェック」です。
ユーザー入力のチェックなど、「想定内のエラー」は if と例外で処理します。
# ユーザー入力のチェック(バリデーション)
if b == 0:
raise ValueError("0 では割れません")
# 開発者の前提チェック(assert)
assert total >= 0, "合計値が負になるのはバグ"
Python外からの入力に対しては「丁寧にエラーを返す」。
自分のコードの前提に対しては「崩れたら assert で即座に気づく」。
この使い分けが大事です。
assert の注意点(特に重要なポイント)
最適化オプションで無効化されることがある
Python は、-O(大文字のオー)オプションを付けて実行すると、assert 文を無視する(削除する)モードになります。
python -O app.py
このモードでは、assert は実行されません。
つまり、「絶対に実行されてほしいチェック」を assert に書くのは危険です。
例えば、これはダメな例です。
assert user_is_admin, "管理者だけが実行できる処理です"
do_admin_only_operation()
Python-O で実行すると、assert が消えてしまい、
管理者チェックなしで do_admin_only_operation() が動いてしまいます。
「セキュリティやビジネスロジックに関わるチェック」は、
必ず if と例外で書きます。
if not user_is_admin:
raise PermissionError("管理者だけが実行できます")
Pythonassert はあくまで「開発時の安全装置」として使う、
という意識を持っておくのがとても重要です。
副作用を持つ処理を assert の中に書かない
これは地味に危険なパターンです。
assert log_and_return_flag() # 中でログを書いている
Pythonlog_and_return_flag() がログを書いたり、何か状態を変えたりする関数だと、-O で assert が消えたときに、その副作用も消えてしまいます。
assert の中には「純粋な条件式」だけを書く、
副作用のある処理は外に出す、というのが安全な書き方です。
まとめ(assert は「前提と期待をコードに刻む道具」)
初心者目線で assert を整理すると、こうなります。
assert 条件 は、「この条件が True である前提」をコードに書くための文。False なら AssertionError で止まる。
テストコードでは、「期待する結果」や「期待する例外」を表現するための中心的な道具になる。
アプリコードでは、「ここは絶対こうなっているはず」という開発者の前提を明示するために使う。ただし、ビジネスロジックやセキュリティチェックには使わない。-O オプションで assert は無効化されるので、「必ず実行されるべき処理」や副作用のある処理を assert の中に書かないことが重要。
