Python私有方法和属性并非真正私有,而是通过命名约定(_name)和名称改写(__name→_Class__name)实现;Mock私有成员必须使用改写后的完整名称,如_Protessor__fetch_data,而非直接__fetch_data。

私有方法和私有属性在Python里根本不是“真私有”
Python没有访问控制关键字(如 private),所谓私有只是靠命名约定:_name 是受保护,__name 会触发名称改写(name mangling)。所以“Mock私有方法”本质是 Mock 经过改写后的真实名字,而不是直接写 __method。
常见错误是直接对 __do_something 打补丁,结果发现 mock 没生效——因为实际在类里它被自动转成了 _ClassName__do_something。
验证方式很简单:在测试前打印 dir(instance) 或 dir(YourClass),找以 _YourClass__ 开头的项。
Mock双下划线私有方法的正确写法
必须用改写后的完整名称打补丁,路径取决于你 mock 的位置(类、实例、模块)。
立即学习“Python免费学习笔记(深入)”;
- 若 patch 类方法(推荐):
@patch('module_name.ClassName._ClassName__private_method') - 若 patch 实例方法(需先创建实例):
mock.patch.object(obj, '_ClassName__private_method') - 若在测试中手动赋值(不推荐但有时快):
obj._ClassName__private_method = MagicMock(return_value='ok')
示例:
class Processor:
def __init__(self):
self.__cache = {}
def __fetch_data(self):
return "real_data"
def run(self):
return self.__fetch_data() + "_processed"
测试时这样 mock 才有效:
@patch('test_module.Processor._Processor__fetch_data')
def test_run_with_mocked_private_method(mock_fetch):
mock_fetch.return_value = 'mocked'
p = Processor()
assert p.run() == 'mocked_processed'
Mock私有属性要小心“动态赋值”陷阱
私有属性(如 self.__value)同样会被改写,但 Mock 属性更常出问题的地方不在名称,而在**赋值时机**。
- 如果私有属性在
__init__中初始化,mock 它通常没意义——你 mock 的是实例创建之后的状态,而逻辑可能已读取过原始值 - 更稳妥的做法是 patch 初始化过程本身,或在
setUp中用mock.patch.object替换整个实例的改写后属性名 - 例如想让
self.__cache返回固定 dict:mock.patch.object(p, '_Processor__cache', {'key': 'stub'})
注意:不能写 mock.patch.object(p, '__cache', ...),这会失败,因为 Python 不会把 __cache 改写成 _Processor__cache 在查找时——只有定义时才改写,访问时得用改写名。
为什么不该执着于 mock 私有成员
真正该测的是公开接口的行为。如果你发现自己必须 mock 私有方法才能测通,大概率说明:
- 这个私有方法其实承担了重要职责,应该抽成独立、可测的公有函数或工具类
- 类职责过重,违反单一职责,导致私有逻辑难以隔离
- 测试在验证实现细节而非契约,一旦重构私有方法名或逻辑,测试就脆性失败
名称改写只是弱约束,不是安全边界。强行绕过它去 mock,往往是在给坏设计擦屁股。


















