
在 Python 单元测试中,若使用 mockito 模拟某个被测类内部调用的函数(如 get_plane_by_id),必须对被测模块中实际引用该函数的位置进行打桩,而非原始定义模块——因为 Python 的导入机制会将函数对象绑定到被测模块的命名空间。
在 python 单元测试中,若使用 `mockito` 模拟某个被测类内部调用的函数(如 `get_plane_by_id`),必须对**被测模块中实际引用该函数的位置**进行打桩,而非原始定义模块——因为 python 的导入机制会将函数对象绑定到被测模块的命名空间。
当你在 PersistAircraftEvents 类中写入:
from models.crud import create_plane, get_plane_by_id
Python 解释器会在该模块(例如 services/aircraft.py 或 persist_aircraft_events.py)的全局命名空间中创建一个对 get_plane_by_id 的直接引用。此时,service.persist_aircraft_event() 内部调用的并非 models.crud.get_plane_by_id,而是当前模块中名为 get_plane_by_id 的函数对象。
因此,以下写法无法生效:
from mockito import when import models.crud when(models.crud).get_plane_by_id(...).thenReturn(None) # ❌ 错误:修改的是 models.crud 下的对象,但被测代码不访问它
✅ 正确做法是:对被测类所在模块(即“module-under-test”,简称 MUT)中导入后的名称进行打桩。
立即学习“Python免费学习笔记(深入)”;
假设你的 PersistAircraftEvents 类定义在 services/persist_aircraft.py 中,则应这样写:
from mockito import when, unstub
from services.persist_aircraft import PersistAircraftEvents # ✅ 导入被测类
import services.persist_aircraft # ✅ 导入其所在模块(关键!)
class TestPersistAircraftEvents:
def test_unknown_aircraft_type(self):
db = mock()
service = PersistAircraftEvents(db)
event = ReceivedAircraftEvent(
event_time=datetime.now(),
aircraft_id="what?",
event_type="test"
)
# ✅ 在 services.persist_aircraft 模块上打桩(此处 get_plane_by_id 是 from-import 的别名)
when(services.persist_aircraft).get_plane_by_id(...).thenReturn(None)
try:
service.persist_aircraft_event(event)
# 断言逻辑...
finally:
unstub() # 推荐:清理 stub,避免污染其他测试⚠️ 注意事项:
- 不要 import models.crud 后对其打桩——除非你在被测类中用的是 models.crud.get_plane_by_id() 这种点号调用方式(即未 from ... import);
- 若被测代码使用 import models.crud,则应 when(models.crud).get_plane_by_id(...);
- ... 是 mockito 的通配符,表示任意参数;也可显式写 when(services.persist_aircraft).get_plane_by_id(any(Session), any(str)).thenReturn(None) 提高可读性;
- 始终在测试后调用 unstub()(或使用 with unstub(): ... 上下文),防止 stub 泄漏影响后续测试。
? 总结:Mockito(及多数 Python Mock 工具)遵循“在哪里用,就在哪里 mock”原则。关注函数在运行时被解析的命名空间,而非其定义位置。这是理解 Python mocking 的核心前提。


















