Mock只读属性必须patch类级别property而非实例,因@property是描述符、无setter;正确方式是用PropertyMock替换类上的property,或通过monkeypatch.setattr设置。

Mock只读属性时为什么patch直接赋值会报AttributeError: can't set attribute
因为@property定义的只读属性没有setter,Python在尝试给实例属性赋值时会调用__set__,而只读property的__set__默认抛出AttributeError。直接对实例做mock_obj.attr = value必然失败。
正确做法是:绕过实例层面,去patch类上的property对象本身,或者用PropertyMock替换整个property描述符。
- 对类级别打补丁:
patch.object(MyClass, 'my_prop', new_callable=PropertyMock) - 若需返回不同值(如多次调用返回不同结果),用
side_effect:PropertyMock(side_effect=[1, 2, 3]) - 避免在测试中对实例属性做
mock_obj.my_prop = ...,这几乎总是错的
用PropertyMock模拟带getter和setter的property时要注意什么
即使原property有setter,测试中若只关心读取行为,仍建议用PropertyMock而非手动构造Mock——它天然兼容getattr/setattr协议,且不会意外触发真实逻辑。
-
PropertyMock(return_value=42)支持obj.prop返回42 - 若需支持写入(比如测试 setter 被调用),加
return_value不够,得显式设mock_prop.setter或用PropertyMock的side_effect捕获赋值 - 注意:直接
mock_prop = PropertyMock(); mock_prop = 123不会调用 setter,必须通过实例赋值触发:obj.prop = 123(此时需确保PropertyMock已挂载到类上)
在unittest.mock.patch中patch类属性 vs 实例属性的区别
mock 只读 property 必须 patch 类(或模块中的类定义),不能 patch 实例——因为 property 是描述符,绑定逻辑发生在类层级。patch 实例只会新增一个同名实例属性,完全绕过 descriptor 协议,导致被测代码读的仍是原始 property。
立即学习“Python免费学习笔记(深入)”;
- ✅ 正确:
@patch.object(TargetClass, 'status', new_callable=PropertyMock) - ❌ 错误:
mock_obj = Mock(spec=TargetClass); mock_obj.status = PropertyMock(return_value='ok')—— 这样mock_obj.status是普通属性,不是 descriptor,且类型检查可能失败 - 若被测代码用
type(obj).status访问,也必须 patch 类,否则拿不到 mock 行为
pytest中用monkeypatch替代patch模拟property的写法
monkeypatch 更轻量,适合简单覆盖,但不自动还原(pytest 自动处理),且不能直接传 new_callable,需手动构造 PropertyMock。
- 推荐写法:
monkeypatch.setattr(MyClass, 'config', PropertyMock(return_value={'debug': True})) - 若需验证 setter 是否被调用,可传一个普通
Mock并赋给MyClass.config.fset,但更稳妥的是用PropertyMock+side_effect捕获参数 - 注意:
monkeypatch.setattr对类属性有效,对实例无效;且不能用于已实例化的对象上的 descriptor 覆盖
真正容易被忽略的是:property 的 mock 行为是否被继承影响。如果子类重写了该 property,patch 父类不会影响子类——得明确 patch 实际被调用的那个类。还有,使用 autospec=True 时 PropertyMock 会被绕过,因为它不是真实的 property 类型,这时候得关掉 autospec 或手动指定 spec。


















