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'
测试结果差异
- patch原定义路径(不生效)
@mock.patch('yhab.blah.SomeClass') def test_mock_of_blah_someclass(mock_some_class): assert some_func() != 'hello'
此时method()调用真实类的实例,测试失败。
- 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命名空间里的这个引用。
- 当patch
yhab.blah.SomeClass时,仅替换了yhab.blah命名空间中的类引用,但yhab.main里的引用仍指向原始真实类,因此some_func()依然实例化真实类。 - 当patch
yhab.main.SomeClass时,直接替换了yhab.main命名空间中的目标引用,此时some_func()调用的是Mock类,自然使用Mock实例及方法。
注:Python导入时不会复制类定义,仅创建引用,问题本质是引用所在的命名空间不同。
文档优化建议
原文档描述未明确点明「命名空间引用」这一核心逻辑,对新手不够直观。建议补充以下内容:
- patch的目标必须是被测代码实际使用对象的命名空间路径,而非对象的原始定义路径
- 针对
from ... import ...和import ...两种导入方式,分别给出正确的patch路径示例
内容的提问来源于stack exchange,提问作者Trenton D. Adams
相关产品推荐
相关产品推荐

