Python mock autospec:替身调用一直成功,为什么真实函数却说参数传错了
测试把一个函数换成普通 Mock 后,调用方漏掉参数也能得到假返回值,测试于是通过;换回真实函数却立刻 TypeError。这时问题可能是替身过于宽松,没有保住真实接口的调用方式。create_autospec 可以从现有函数复制签名,让一部分接口变化在测试阶段出现。
下面的 render 要求 label 按位置传入,width 只能按关键字传入。完整脚本需要 Python 3.8 及以上,使用标准库,不安装依赖,不调用外部服务。保存为 demo.py,运行 python demo.py。
AI模型生成概念图:测试夹具核对插入位置与形状,额外部件不能随意塞入;代表调用签名约束,不代表业务数据已经正确。
from unittest.mock import Mock, create_autospec
real_calls = []
def render(label: str, /, *, width: int = 8):
real_calls.append((label, width))
return label[:width]
loose = Mock(return_value="preview")
assert loose() == "preview"
print("plain mock accepted missing label")
strict = create_autospec(render, return_value="preview")
assert strict("abcdef", width=3) == "preview"
strict.assert_called_once_with("abcdef", width=3)
print("autospec accepted the declared call")
checks = [
("missing", lambda: strict()),
("positional width", lambda: strict("abc", 3)),
("keyword label", lambda: strict(label="abc")),
]
for label, call in checks:
try:
call()
except TypeError:
print(label + ": TypeError")
else:
raise AssertionError("invalid call was accepted: " + label)
assert strict(42, width="wide") == "preview"
assert real_calls == []
print("wrong value types accepted; real calls:", real_calls)
try:
render(42, width="wide")
except TypeError:
print("real implementation with wrong values: TypeError")
else:
raise AssertionError("the real implementation unexpectedly succeeded")先限制实参怎样对上形参
普通 Mock 没有必需的 label 也返回 preview。autospec 创建的替身则接受 strict('abcdef', width=3),并通过 assert_called_once_with 验证这次调用。这里返回值是我们手动设置的字符串,真正受检查的是调用形状,不是渲染结果。
随后的三条失败路径分别为 missing、positional width、keyword label,全部得到 TypeError。它们对应缺少必需参数、把仅限关键字的 width 放到位置参数、以及把仅限位置的 label 写成关键字。函数签名里的斜杠和星号不只是文档样式,替身也会按这些边界检查。
签名通过不表示类型与内容通过
strict(42, width='wide') 仍返回 preview。两个参数都占了合法位置,因此签名匹配成功;注解 str、int 没有变成运行时的数据验证器。紧接着 real_calls 为 [],证明到此为止替身没有执行 render 的函数体。
真正调用 render(42, width='wide') 时才发生 TypeError。这条对照提醒我们:使用 autospec 仍需为输入类型、范围、返回内容以及错误响应分别设计测试。不要拿替身的设定返回值,来证明真实实现能处理同一组业务数据。
替身需要跟着你要模拟的接口建立
例子直接从当前 render 建立规格,所以调用方与函数声明不一致时能尽早暴露。真实项目里,可以为传入的依赖对象建立 autospec,或在 patch 时使用 autospec=True;替换位置仍要对应被测代码实际查找的名称,否则替身根本不会被用到。
自动检查也有边界。对复杂对象建立规格可能需要访问属性,具有副作用的属性或动态创建的成员值得单独审查;只有实例初始化时才出现的属性,也不能总靠类对象的规格完整推知。本文只对普通函数建立替身,避免把这些对象探索问题混进基本调用实验。
把接口测试和真实行为测试配合使用
适合用 autospec 的场景包括依赖重命名参数、增加必需项或把某项改为仅限关键字之后,防止调用方继续用旧方式。迁移时先选一个重要依赖,让测试同时覆盖一次合法调用和一个代表性的错误调用,再保留少量真实实现的行为测试。不要为了追求更严格就把所有替身内部细节都断言一遍;检查应对应调用方真正依赖的契约。
资料核对日期:2026年10月2日。本文固定输入示例在本地 Python 3.12.14 实际执行,退出码为0,预期结果检查通过;这是语言行为演示,不是生产压测。


