
本文详解如何使用 unittest.mock.patch 正确模拟类实例方法,避免 AttributeError: object has no attribute 错误,并可靠验证目标方法是否被调用。
本文详解如何使用 unittest.mock.patch 正确模拟类实例方法,避免 attributeerror: object has no attribute 错误,并可靠验证目标方法是否被调用。
在 Python 单元测试中,当被测类(如 A)依赖另一个类(如 B)的实例方法(如 bar())时,若直接实例化真实对象并尝试手动替换属性(如 self.mock_b_methods),极易因对象结构不匹配而触发 AttributeError: 'A' object has no attribute 'b' —— 这通常是因为 A 实例在初始化时未成功创建 b 属性(例如条件逻辑未满足、构造参数错误或 b() 工厂函数未正确定义),或 mock 未注入到正确作用域。
根本解决方案是:使用 @patch.object 在测试前动态替换目标类的方法,而非手动构造 mock 并赋值给被测实例。 这样既无需关心 A 的内部初始化细节,也避免了属性缺失风险。
以下为推荐的、可直接运行的完整示例:
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() 是工厂函数,此处简化为直接实例化;若需保留工厂函数,patch 应作用于该函数而非 B 类
def foo(self):
return self.b.bar()
class TestA(TestCase):
@patch.object(B, 'bar') # ✅ 关键:在 B 类层面打补丁,所有 B 实例的 bar 方法均被替换为 Mock
def test_foo_calls_b_bar(self, mock_bar):
# Arrange
mock_bar.return_value = True
a = A() # 正常初始化,self.b 被创建且类型为 B
# Act
result = a.foo()
# Assert
self.assertTrue(result)
mock_bar.assert_called_once() # 验证 bar 方法被调用一次✅ 为什么
@patch.object(B, 'bar')有效?
它在测试执行前将B.bar动态替换为MagicMock,因此无论A.__init__如何创建self.b(只要其类型是B或其子类),self.b.bar()调用都会命中 mock,不会触发AttributeError。
⚠️ 常见误区与注意事项:
立即学习“Python免费学习笔记(深入)”;
- ❌ 不要手动创建
mock_b_methods后赋值给A实例(如self.A.b = self.mock_b_methods),这会绕过A.__init__的原始逻辑,且易因属性名/时机错误导致失败; - ❌ 避免在
setUp中提前实例化A(),因为此时patch尚未生效——补丁仅在被装饰的测试方法执行期间生效; - ✅ 若
A通过工厂函数b()获取B实例(如原题所示),应改为@patch('__main__.b')(路径需与实际导入位置一致),并确保b()返回的是 mock 对象; - ✅ 使用
assert_called_once()或assert_called_with(...)可进一步验证调用次数与参数,提升测试健壮性。
总结:解决此类 AttributeError 的核心在于让 mock 作用于依赖的“源头”(类或模块),而非试图修补被测对象的中间状态。@patch.object 是最简洁、最符合测试隔离原则的方式,它使测试专注行为验证,而非实现细节。


















