
本文详解如何使用 unittest.mock.patch 正确模拟类方法调用,避免 AttributeError: object has no attribute 错误,并可靠验证目标方法是否被触发。
本文详解如何使用 `unittest.mock.patch` 正确模拟类方法调用,避免 `attributeerror: object has no attribute` 错误,并可靠验证目标方法是否被触发。
在 Python 单元测试中,当被测类(如 A)依赖另一个类(如 B)的实例方法(如 bar())时,若未合理处理依赖关系或模拟时机,极易出现 AttributeError: 'A' object has no attribute 'b'。该错误的根本原因在于:测试中创建 A() 实例时,其 __init__ 逻辑未能成功初始化 self.b 属性——例如因条件分支未覆盖、外部函数 b() 未正确定义或模拟未生效所致。
正确的解决方案是在目标方法被调用前,对依赖对象的行为进行精准打补丁(patch),而非手动构造 mock 并尝试赋值给未存在的属性。推荐使用 @patch.object 装饰器,它在测试方法执行前自动将指定类的方法替换为 MagicMock,并在测试结束后自动还原,安全且语义清晰。
以下为可直接运行的完整示例:
from unittest import TestCase
from unittest.mock import patch
class B:
def bar(self):
return True
class A:
def __init__(self, has_b=True):
if has_b:
self.b = B() # 注意:原问题中此处调用的是未定义的 b() 函数,应改为 B()
def foo(self):
return self.b.bar()
class TestA(TestCase):
@patch.object(B, 'bar') # ✅ 关键:直接 patch B 类的 bar 方法
def test_foo_calls_b_bar(self, mock_bar):
mock_bar.return_value = True
a = A() # 正常初始化,self.b 是真实 B 实例(但其 bar 已被 mock 替换)
result = a.foo()
# 验证行为
self.assertTrue(result)
mock_bar.assert_called_once() # ✅ 成功断言 bar 被调用⚠️ 注意事项:
立即学习“Python免费学习笔记(深入)”;
- 不要在
setUp中提前创建被测实例:若A()在setUp中初始化,而@patch尚未生效,self.b可能仍为None或引发 AttributeError。务必在@patch作用域内(即测试方法体内)创建实例。- 修复原始代码逻辑缺陷:原问题中
self.b = b()调用了一个未定义的全局函数b(),实际应为self.b = B();若需保留工厂函数,须确保其在测试上下文中可用且不干扰 patch 行为。- 优先 patch 类,而非实例:
@patch.object(B, 'bar')作用于所有B实例的bar方法,比手动setattr(A(), 'b', mock_b)更鲁棒,也避免了AttributeError。- 验证调用而非返回值:
assert_called_once()直接验证“是否调用”,比仅检查返回值更能体现测试意图——这正是你最初想断言的核心行为。
总结:解决此类问题的关键,在于理解 mock.patch 的作用时机与作用域。通过装饰器方式 patch 目标类的方法,让测试聚焦于“交互契约”(即“A.foo() 是否触发了 B.bar()”),而非纠结于实例属性的初始化细节,既提升测试稳定性,也增强代码可维护性。


















