良いテストコードの書き方
実行されたかではなく、実行状態を検証する¶
関数が呼び出されたことだけ確認すると、
適切な引数で呼ばれてない場合
本当は1回でいいのに2回以上呼ばれてる場合
不要な関数が呼ばれてる場合
に気付けない
# ❌️ 関数が実行されたことを確認するだけ
def test_register_success(mocker):
notifier = mocker.Mock()
service = UserService(notifier)
service.register(success=True)
# 悪い例:対象が呼ばれたことだけ確認
# 引数、回数、不要な呼び出しの有無を検証していない
assert notifier.success.called不要な関数が呼ばれていないことも検証すべき。
# ⭕️ 相互作用の契約を検証する
def test_register_success(mocker):
notifier = mocker.Mock()
service = UserService(notifier)
service.register(success=True)
# 適切な引数で、適切な回数呼び出されたことを確認する
notifier.success.assert_called_once_with(user_id=123)
# negative assertion: 対象外のものが呼ばれてないことを確認する
notifier.error.assert_not_called()他にも、例えばDBからのデータ取得処理を検証したいなら、「欲しいデータが得られている」だけでなく「対象外のデータが得られない」「データを変更しない」ことも検証すべき。
一部ではなく、期待値の全体を検証する¶
# ❌ フィールド単体しか検証していない
assert result["id"] == 1
assert result["name"] == "Alice"
assert result["role"] == "member"# ✅ 構造全体を固定する
# 意図しないキーの追加・削除や値の変更も検知できる
assert result == {
"id": 1,
"name": "Alice",
"role": "member",
}
# ✅ フィールド単体に加えて、キーもチェックする
# キーの過不足も検知できる
assert set(result.keys()) == {"id", "name", "role"}
assert result["id"] == 1
assert result["name"] == "Alice"
assert result["role"] == "member"