应避免过度使用 mock,只对网络、数据库、时间等不可控边界进行 mock;mock 位置须为被测代码导入路径而非定义路径;协程函数必须用 asyncmock;mock 返回值需动态构造以覆盖分支逻辑。

mock 用太多,测试变脆弱了
过度使用 mock 会让测试严重依赖实现细节,而不是行为契约。一旦函数内部调用链微调(比如改了个辅助函数名、拆了个逻辑块),一堆测试就红——但业务逻辑其实没坏。
常见错误现象:AttributeError: Mock object has no attribute 'xxx' 或 AssertionError: Expected call not made,不是因为功能出错,而是因为 mock 的路径或调用次数和实际执行路径对不上。
- 只在「无法控制的边界」mock:比如网络请求(
requests.get)、数据库驱动(sqlite3.connect)、时间(datetime.now) - 别 mock 同一模块里的其他函数——它们是你的协作单元,应该一起跑;否则等于把单元测试写成了“伪集成”
- 如果发现要 mock 5 个以上对象才能让一个测试通过,大概率是函数职责太重,该拆了
patch 的作用域搞错,mock 失效
mock 生效位置必须是「被测试代码导入并使用的路径」,不是定义路径。很多人在函数定义处 patch,结果白忙活。
比如你写了 from utils import format_date,然后在 main.py 里调用了它,那 patch 必须写成 @patch('main.format_date'),而不是 @patch('utils.format_date')。
立即学习“Python免费学习笔记(深入)”;
在原版的基础上做了一下修正评论没有提交正文的问题特价商品的调用连接问题去掉了一个后门补了SQL注入补了一个过滤漏洞浮动价不能删除的问题不能够搜索问题收藏时放入购物车时出错点放入购物车弹出2个窗口修正主题添加问题商家注册页导航连接问题销售排行不能显示更多问题热点商品不能显示更多问题增加了服务器探测 增加了空间使用查看 增加了在线文件编辑增加了后台管理里两处全选功能更新说明:后台的部分功能已经改过前台
- 查失效原因,先 print 出被测函数体内的
format_date对象 ID:print(id(format_date)),再 print mock 对象 ID,看是不是同一个 - 用
autospec=True能提前暴露签名不匹配问题,比如 mock 了带参数的函数却没设return_value - 避免在类方法里用
patch.object(self, 'xxx')——self 是实例,patch.object 会 patch 实例属性,不是类方法
mock 返回值写死,掩盖真实逻辑分支
用 return_value=42 硬编码返回值,容易让测试漏掉条件判断路径。更糟的是,当真实函数返回 None 或空列表时,mock 却返回非空,导致 if 分支永远不进。
典型场景:mock db.query() 总返回 [{'id': 1}],结果 if not results: 分支压根没被覆盖。
- 按测试目标动态构造返回值:用
side_effect返回不同值,或抛异常模拟失败路径 - 对返回结构复杂的对象(如 ORM 模型),优先用真实轻量对象(如
namedtuple或types.SimpleNamespace),别全用Mock() - 检查覆盖率报告里的 branch coverage,特别盯住
if/else、try/except是否都被触发过
async 函数里用普通 mock,await 直接卡住
对协程函数(async def)打普通 Mock() 补丁,调用时不会自动 await,而是返回一个 Mock 对象——接着你就看到 RuntimeWarning: coroutine 'Mock' was never awaited,或者测试挂起不动。
根本原因是:普通 mock 不是协程,不能被 await。
- 必须用
AsyncMock()替代Mock(),它实现了__await__,能被正常 await - 如果用
patch,记得传new_callable=AsyncMock,例如:@patch('api.fetch_data', new_callable=AsyncMock) -
side_effect里若要抛异常,得抛在 await 之后,所以推荐用return_value=coroutine或side_effect=[some_coro, ...],别直接 return 异常
最常被忽略的一点:mock 不是万能胶,它解决不了设计问题。当你需要 mock 一堆内部函数才能测通一个方法时,那个方法本身已经越界了——它既做决策,又管 IO,还操心格式化。这时候删掉 mock,先重构函数,比补十个 patch 更省时间。









