You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python unittest.mock调用参数是否按引用拷贝而非保留调用时快照?

问题结论

你遇到的行为不是unittest.mock.Mock的bug,是官方有意设计的默认行为。

原因说明

  • Python所有函数参数默认按对象引用传递,Mock不会额外拷贝传入参数,是出于通用性和性能的考量:deepcopy操作存在不可忽略的性能开销,且部分对象(打开的文件句柄、数据库连接、系统资源实例等)无法被深拷贝,如果默认开启深拷贝逻辑,会导致大量场景下Mock直接抛出异常,无法正常使用。
  • 你遇到的单元测试误判问题,本质是断言时机错误:对Mock调用参数的校验应该在可变参数被修改之前执行,不应该等到参数对象被后续逻辑改动后再做校验,这不符合单元测试的最佳实践。

解决方案

如果你的测试场景需要留存调用时的参数快照,可以采用两种方式处理:

  1. 调用Mock后立刻执行断言,或者手动留存参数快照:
from unittest.mock import Mock
from copy import deepcopy

mock = Mock()
l = [1]
mock(l)
# 方案1:调用后立刻做断言
assert mock.call_args[0][0] == [1]
# 方案2:调用后立刻深拷贝留存参数快照
call_snapshot = deepcopy(mock.call_args)

l.append(2)
# 后续用留存的快照做校验
assert call_snapshot[0][0] == [1]
  1. 自定义自动深拷贝参数的Mock子类,适配频繁处理可变参数校验的场景:
from unittest.mock import Mock
from copy import deepcopy

class SnapshotMock(Mock):
    def __call__(self, /, *args, **kwargs):
        # 调用时先深拷贝所有参数再传递给父类存储
        copied_args = deepcopy(args)
        copied_kwargs = deepcopy(kwargs)
        return super().__call__(*copied_args, **copied_kwargs)

关于默认使用deepcopy的合理性

默认修改call_args为深拷贝存储参数的方案并不合理,该提议已经被Python核心开发团队拒绝:修改默认行为会破坏存量代码的向下兼容性,且无法适配所有参数类型,带来的收益远低于造成的兼容性成本。

内容的提问来源于stack exchange,提问作者Evan Lesmez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 10:45:04