Python|mock活用で副作用遮断責任を整理する技法
この記事のポイント
- mockを使った副作用遮断設計の責任範囲をレビュー視点で整理できる
- mock対象の選定基準・整理技法を学べる
- 実務での副作用管理文化を体系化できる
- PlantUMLで副作用依存構造の整理イメージを可視化できる
そもそもmockとは
Pythonのunittest.mock(またはpytest-mock)は「外部依存の疑似置換」を提供するモジュールです。
- 外部API・DB・ファイルIOなどの副作用遮断
- 振る舞いの検証(呼び出し確認)
- 戻り値の制御
- 例外発生の疑似注入
mock基本例
from unittest import mock
def external_call():
return "real data"
def logic():
data = external_call()
return data + " processed"
def test_logic():
with mock.patch("__main__.external_call", return_value="mock data"):
assert logic() == "mock data processed"mock.patch()で対象を差し替え- 本物の外部依存を実行せず、テストで疑似制御
なぜこれをレビューするのか
mock活用は「副作用遮断=設計境界の可視化」に直結します。
レビューアーは以下の観察視点を持つ必要があります。
レビューアー視点
- mock対象が適切な副作用単位に整理されているか
- mockが過剰/不足になっていないか
- mock依存でテストがブラックボックス化していないか
- mock経路の指定が設計境界を反映しているか
- mock副作用発生が抑制できているか
開発者視点
- どこをmockすべきか曖昧になりがち
- 便利なので上位層までmockしてしまいがち
- 結果として意味の薄い「存在確認テスト」になる
- 実質的なロジック検証が空洞化しがち
mock設計崩壊の典型設計臭
崩壊しやすいパターン
- インフラ層より上位ロジック層までmock多用
- 結果比較ではなくmock呼出回数だけ検証
- mock依存が肥大し、実ロジック未検証化
- mock対象のモジュール名指定が曖昧化
- 上位APIのmock内ネストが複雑化
崩壊例:ロジック空洞化型
呼出検証偏重型
from unittest import mock
def send_mail(address):
# 実際のメール送信
pass
def register_user(user_id, email):
# ユーザ登録ロジック
send_mail(email)
def test_register_user():
with mock.patch("__main__.send_mail") as mock_mail:
register_user("alice", "[email protected]")
mock_mail.assert_called_once_with("[email protected]")
@Reviewer呼出確認のみで登録ロジック自体は未検証です。結果検証主体に整理しましょう。
問題点
- send_mail呼出しか確認しておらず登録成否は未検証
- 意味の薄い「mock存在確認テスト」になる
崩壊構造モデル:mock偏重依存
mock整理の設計原則
mockは「設計責務境界の写像」で使います。
以下の整理文化が有効です。
① 副作用遮断目的の明示化
- 通信・DB・ファイルIO・時間依存
- 外部API契約部のみ遮断
② ロジック層はmockせず実行
- 内部純粋計算は本物で通す
③ 戻り値制御 vs 呼出検証のバランス
- 戻り値制御で正常系/異常系を網羅
- 呼出検証は仕様契約部に限定
④ モジュール境界準拠の指定文化
mock.patch("パス")の指定はimport構造準拠
⑤ ネスト抑制文化
- mock入れ子指定は可能な限り浅く制御
改善例:責務分離版
mock整理版
from unittest import mock
def send_mail(address):
# メール送信処理
pass
def register_user(user_id, email):
# DB登録ロジック
if email:
send_mail(email)
return True
def test_register_user_success():
with mock.patch("__main__.send_mail"):
result = register_user("alice", "[email protected]")
assert result
def test_register_user_without_email():
result = register_user("alice", None)
assert result「ロジック実行は本物、外部だけ差し替え文化」
レビューアーは「mock境界文化」を徹底指導すると設計崩壊を抑止できます。
改善構造モデル:責任分離整理
良くない実装例: ケース1(過剰ネスト)
過剰ネスト例
with mock.patch("__main__.send_mail") as mail_mock, \
mock.patch("__main__.create_user") as user_mock, \
mock.patch("__main__.update_profile") as profile_mock:
...
@Reviewermock入れ子が過剰です。mock範囲を設計責務単位に縮小整理してください。
問題点
- 読解難易度激増
- テスト意図不透明化
良くない実装例: ケース2(境界崩壊)
境界違反例
with mock.patch("external_api.send_mail") as mail_mock:
...
@Reviewerimport経路に準拠せず直接外部パスを指定しています。呼出元モジュールパスに合わせてください。
問題点
- patch対象が正しく解決されない
- メンテ負荷増大
改善例:import経路準拠
import構造準拠版
# main.pyで import external_api
from external_api import send_mail
# テスト時は
with mock.patch("main.send_mail"):
...- テスト側もimport経路準拠
- patch対象の解決が安定
観点チェックリスト
まとめ
mock設計は「責務境界の写像精度で崩壊度が決まる」領域です。
レビューアーは「mock文化・副作用遮断文化・設計境界準拠文化」を育成指導することで、長期保守に耐える安全なテスト設計を築けます。

