商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Python 中使用 Mockito 正确模拟类内导入的单个函数方法

Python 中使用 Mockito 正确模拟类内导入的单个函数方法

  发布于2026-05-21 阅读(0)

扫一扫,手机访问

Python 中使用 Mockito 正确模拟类内导入的单个函数方法

在 Python 单元测试中,用 mockito 模拟某个方法失败,往往不是因为语法错误,而是因未在被测模块的命名空间中打补丁——必须 mock 被测类实际引用的函数路径(即 from models.crud import get_plane_by_id 后在类中直接调用的 get_plane_by_id),而非原始定义位置。

你有没有遇到过这种情况?在 Python 单元测试里,明明用 mockito 把某个函数给“替换”掉了,可一运行测试,它还是固执地调用了原始实现。语法检查没问题,但模拟就是不起作用。

问题往往不在于代码写错了,而在于“打补丁”的位置没找对。核心在于,你必须模拟被测模块实际使用的那个函数引用,而不是函数最初定义的那个地方。

问题的根源:Python 的导入与命名空间

这背后的关键,是 Python 的导入机制。举个例子,假设你的被测类 PersistAircraftEvents 定义在 services/aircraft.py 文件里,并且文件开头有这样一行导入语句:

from models.crud import get_plane_by_id, create_plane

当这行代码执行时,get_plane_by_id 这个函数对象就从 models.crud 模块被绑定到了当前模块 services.aircraft 的局部命名空间。此后,在 PersistAircraftEvents 类内部,你调用的 get_plane_by_id(...),实际上指向的是 services.aircraft.get_plane_by_id 这个引用,而不再是 models.crud.get_plane_by_id

所以,如果你在测试中去模拟 models.crud.get_plane_by_id,就等于在错误的战场上作战,自然无法拦截到实际发生的调用。

✅ 正确的做法:在被测模块的命名空间中模拟

正确的思路是:找到函数被实际使用的位置,然后在那里“动手脚”。具体操作如下:

from mockito import when, unstub
from services.aircraft import PersistAircraftEvents  # ✅ 导入被测类
import services.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.aircraft 模块中导入的那个函数
        when(services.aircraft).get_plane_by_id(...).thenReturn(None)

        try:
            service.persist_aircraft_event(event)
            # 这里添加你的断言逻辑...
        finally:
            unstub()  # 清理 mock,避免污染其他测试

看到了吗?关键在于 import services.aircraft 这一行,然后我们针对 services.aircraft.get_plane_by_id 进行模拟。这才是函数在被测类眼中真正的“模样”。

⚠️ 常见误区与注意事项

为了让这个知识点更清晰,我们不妨梳理几个典型的错误和对应的正确做法:

  • ❌ 错误路径when(models.crud).get_plane_by_id(...)。这修改的是源模块,但调用发生在另一个命名空间里,所以模拟无效。
  • ❌ 重复导入:先 from models.crud import get_plane_by_id,再 import models.crud 并尝试模拟后者。这依然无效,因为第一次导入已经创建了一个独立的引用。
  • ✅ 正确做法:务必导入被测类所在的模块(本例中是 services.aircraft),并对其内部的函数引用进行打补丁。
  • ✅ 保持测试隔离:使用 unstub()(建议放在 finally 块中)来清理模拟,这在像 pytest 这样测试用例共享进程上下文的框架中尤为重要。
  • ? 其他工具:如果你使用 pytest,也可以结合其内置的 monkeypatch 夹具达到类似效果。但无论使用哪种工具,记住“在调用发生的地方进行模拟”这个核心原则总是最可靠的。

总结

说到底,Mock 的本质是劫持运行时调用链上的具体目标。在 Python 中,from X import Y 这种语法会创建一个指向 Y 的新引用。因此,你的模拟操作必须发生在这个新引用被创建和使用的地方——也就是导入它的那个模块内部。

找准这个“落地点”,你的模拟就能精准命中目标。下次再遇到模拟失效的问题,不妨先问自己一句:“我模拟的,真的是它正在调用的那个东西吗?”

本文转载于:https://www.php.cn/faq/2444674.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注