
本文介绍在 Python 单元测试中,如何准确断言一个类的实例方法(如 obj.do)是否被正确传递给模拟对象的方法(如 submit()),关键在于区分绑定方法与原始函数,并通过 __func__ 属性进行可靠比对。
本文介绍在 python 单元测试中,如何准确断言一个类的实例方法(如 `obj.do`)是否被正确传递给模拟对象的方法(如 `submit()`),关键在于区分绑定方法与原始函数,并通过 `__func__` 属性进行可靠比对。
在使用 unittest.mock 或 pytest-mock 进行行为驱动测试时,常需验证某个实例方法(bound method)是否被作为回调参数传入模拟函数(例如 ThreadPoolExecutor.submit)。直接比较 mock_call.args[0] == MyTask.do 会失败,因为前者是绑定到具体实例的 <bound method mytask.do of object at ...>></bound>,而后者是未绑定的类函数 <function mytask.do at ...></function> —— 二者类型与内存地址均不同。
✅ 正确验证方式:使用 __func__ 属性
每个绑定方法对象都具有 __func__ 属性,它指向其底层的原始函数对象。因此,应比较:
assert mock_call.args[0].__func__ is MyTask.do
注意:推荐使用 is 而非 ==,以确保是同一函数对象(避免意外重载或装饰器干扰)。
? 完整测试示例(pytest + unittest.mock)
import pytest
from unittest import mock
import concurrent.futures
# 假设 MyTask 和 Manager 已定义(见问题描述)
def test_run_task_passes_do_method():
manager = Manager()
# 替换 submit 为 Mock
manager.worker_executor.submit = mock.Mock()
# 执行待测逻辑
manager.run_task()
# 验证 submit 被调用,且第一个参数是 MyTask.do 的绑定形式
assert manager.worker_executor.submit.called
call = manager.worker_executor.submit.call_args
assert len(call.args) >= 1
bound_method = call.args[0]
assert hasattr(bound_method, '__func__')
assert bound_method.__func__ is MyTask.do # ✅ 关键断言⚠️ 注意事项与边界情况
-
继承场景兼容:若子类未重写
do方法(如OtherTask(MyTask)),OtherTask().do.__func__ is MyTask.do仍为True,适用于泛化校验。 -
不适用于重写方法:若子类覆盖了
do,则OtherTask.do是独立函数,__func__将指向子类版本,此时需按需调整断言目标。 -
避免依赖
__func__的实现细节?
尽管__func__是 CPython 的稳定公开属性(PEP 232 规范),但严格来说属于“协议约定”而非强制 API。不过在所有主流 Python 实现(CPython、PyPy)及当前所有稳定版本中均可靠可用,实践中广泛采用。 -
不适用场景:
- 内置方法(如
list.append)无__func__(而是__wrapped__或直接不可达); - 使用
functools.partial或闭包包装的方法; - 经
@staticmethod/@classmethod修饰的方法需分别访问__func__或__func__(@classmethod的绑定方法__func__指向原函数,@staticmethod则本身就是函数)。
- 内置方法(如
✅ 更健壮的断言封装(可选)
为提升可读性与复用性,可封装辅助断言:
def assert_bound_method_calls(func, bound_method):
"""断言 bound_method 是 func 的绑定实例方法"""
assert callable(bound_method), f"{bound_method!r} is not callable"
assert hasattr(bound_method, '__func__'), f"{bound_method!r} has no __func__"
assert bound_method.__func__ is func, \
f"Expected {func}, got bound method of {bound_method.__func__}"
# 使用:
assert_bound_method_calls(MyTask.do, mock_call.args[0])通过理解绑定方法的内部结构并合理利用 __func__,即可精准、可靠地完成此类高价值的行为验证,显著增强异步任务调度、回调注册等场景的测试覆盖率与信心。

















