You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

unittest.mock.patch异常行为:为何不同路径打桩效果不同?

关于unittest.mock.patch的路径问题解答

问题场景

代码结构

  • yhab.main模块代码:
from yhab.blah import SomeClass

def some_func():
    some_instance = SomeClass()
    return some_instance.method()
  • yhab.blah模块中SomeClass的定义:
class SomeClass:
    def method(self):
        return 'hello'

测试结果差异

  1. patch原定义路径(不生效)
@mock.patch('yhab.blah.SomeClass')
def test_mock_of_blah_someclass(mock_some_class):
    assert some_func() != 'hello'

此时method()调用真实类的实例,测试失败。

  1. patch导入后的命名空间路径(生效)
@mock.patch('yhab.main.SomeClass')
def test_mock_of_main_someclass(mock_some_class):
    assert some_func() != 'hello'

此时method()调用Mock实例,测试通过。

疑问:为什么两种patch路径结果不同?曾猜测Python导入时会复制类定义,但测试否定了该猜测。

原文档相关描述(已翻译):

对类进行patch会用MagicMock实例替换该类。如果被测代码中实例化了这个类,那么会使用mock的return_value(即mock实例)。

该描述对Python新手不够友好,是否需要更新文档使其更清晰?


原因解析

mock.patch的核心逻辑是替换对象在其被使用的命名空间中的引用,而非替换对象的原始定义位置。

在yhab.main模块中,from yhab.blah import SomeClass的导入方式,会在yhab.main的命名空间内创建一个指向yhab.blah.SomeClass的引用。当some_func()执行SomeClass()时,实际调用的是yhab.main命名空间里的这个引用。

  • 当patchyhab.blah.SomeClass时,仅替换了yhab.blah命名空间中的类引用,但yhab.main里的引用仍指向原始真实类,因此some_func()依然实例化真实类。
  • 当patchyhab.main.SomeClass时,直接替换了yhab.main命名空间中的目标引用,此时some_func()调用的是Mock类,自然使用Mock实例及方法。

注:Python导入时不会复制类定义,仅创建引用,问题本质是引用所在的命名空间不同。


文档优化建议

原文档描述未明确点明「命名空间引用」这一核心逻辑,对新手不够直观。建议补充以下内容:

  • patch的目标必须是被测代码实际使用对象的命名空间路径,而非对象的原始定义路径
  • 针对from ... import ...和import ...两种导入方式,分别给出正确的patch路径示例

内容的提问来源于stack exchange,提问作者Trenton D. Adams

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:30:48