Mock技术概述:知己知彼,百战不殆

做自动化测试这么多年,我越来越觉得“隔离”是个大学问。你要测A模块,结果B、C、D都跟着跑,出了问题你根本搞不清是A的锅还是B的锅。这时候,Mock技术就是你的救星。

说白了,Mock就是用一个“替身”来替换掉被测代码的依赖。这个替身能模拟真实对象的行为,但又不依赖真实环境。嗯,这就好比拍电影时用替身演员——危险动作让替身上,主角安全第一。

1. Mock对象到底是什么?

Mock对象,简单讲就是一个轻量级的“假”对象。它只关心两件事:

  • 你调用了它的哪些方法
  • 你传入的参数是否正确

至于它内部有没有数据库、会不会发网络请求,统统不关心。我早期做微服务测试时,经常遇到一个服务依赖另一个服务,对方还没开发完。怎么办?Mock!先模拟返回假数据,等对方上线再替换。

核心定义:Mock对象是测试替身的一种,它通过预设期望(expectation)来验证被测代码与依赖之间的交互行为是否如预期。

举个例子,假设我们有个支付服务 PaymentService,它要调用 PaymentGateway 的 charge() 方法。用Mock的话就是这样:

# 假设用 unittest.mock
from unittest.mock import Mock

gateway = Mock()
gateway.charge.return_value = True   # 预设返回值

service = PaymentService(gateway)
result = service.pay(100)

assert result is True
gateway.charge.assert_called_once_with(100)  # 验证调用

看到了吗?我们根本没连真正的支付网关,只关心charge是不是被正确调用了。我曾经踩过一个坑——测试里忘了断言参数,结果上线后发现传错了金额。所以,断言调用参数这一步千万别省。

2. Mock vs Stub vs Fake:别再傻傻分不清

很多初学者搞不清这几个概念,其实它们都是“测试替身”家族的不同成员。我用一个表格帮大家理清楚:

术语 核心作用 有无行为验证? 举个实际例子
Mock 验证交互(调用了谁、传了什么参数) 有 检查日志记录器是否输出了错误信息
Stub 提供预设返回值,不关心调用过程 无 数据库查询返回固定列表
Fake 轻量级真实实现(有逻辑但不用真正基础设施) 无(通常) 用 Python 字典模拟内存数据库

你想想看,Mock 像不像一个“监工”?它不光干活,还记录你喊了他几次、喊错没有。Stub 更像一个“甩手掌柜”——你说要返回什么,它就返回什么,绝不啰嗦。而 Fake 则是一个“迷你版”真实实现,比如用内存里的 dict 模拟 Redis——我用过很多次,跑单元测试飞快。

个人经验:我刚做测试时,觉得 Stub 就行,没必要用 Mock。直到有一次重构接口,Stub 返回的数据格式变了,测试却全绿。上线后炸了……那之后我才明白:该验证交互的时候,就得用 Mock。

3. 什么时候该用 Mock?

不是所有场景都需要 Mock。我总结了几个最常见的“菜谱”,你看看哪个匹配你的情况:

  1. 依赖还没写好:比如前端调后端接口,后端还没开发完。Mock 一个假接口,前端先跑起来。
  2. 外部服务不可控:调用第三方 API、发短信、支付网关……真实服务可能收费、限流、不稳定。Mock 掉,测试稳稳的。
  3. 模拟异常场景:比如网络超时、数据库连接失败。真实环境很难触发。用 Mock 抛出异常,一秒搞定。
  4. 提升测试速度:每次跑测试都去读写数据库、发 HTTP 请求,慢到怀疑人生。Mock 之后,毫秒级跑完。
  5. 隔离问题:只测当前模块,不依赖上下游。这样出错了,你一眼就能定位。

注意:Mock 不是万能药。过度 Mock 会导致测试与真实环境脱节,出现“测试全绿,线上全红”的悲剧。我曾经在一个项目里把所有外部依赖都 Mock 了,结果部署时发现真实 API 改了字段名……从那以后我记住了:Mock 只用于单元测试和集成测试的临界点,端到端测试必须用真实服务。

4. 一张图理清 Mock 技术全貌

下面这张 SVG 图展示了 Mock 在整个测试替身家族中的位置,以及它的使用流程。你一眼就能看清:

Mock 技术知识图谱 测试替身(Test Double) Mock Stub Fake 行为验证 预设期望 调用断言 使用场景:依赖未就绪 / 外部不可控 / 异常模拟 / 加速测试 预设返回值 轻量级实现 Mock 关注“是否按预期交互”,Stub 关注“返回什么”,Fake 关注“简单实现功能” 核心区别:Mock 有行为验证,Stub/Fake 通常没有

5. 实战中的一个小技巧

如果团队里有人问“为什么不用 Fake 代替 Mock?”,我通常会这样回答:

  • Fake 有逻辑,会引入额外的 bug 可能;Mock 只记录调用,逻辑极简。
  • Mock 天生支持验证次数和参数,Fake 你还要自己写 assert。何必呢?

我个人习惯是:外部依赖(网络、数据库)用 Mock;内部工具类(时间工具、加密工具)用 Stub;需要完整模拟某种状态但不想搭真实环境时用 Fake。这样分工,测试又快又准。

一句话总结:Mock 是测试的“摄像头”,帮你盯着依赖的每一次调用。别滥用,也别不用——用对了地方,它就是 CI 自动化回放里最可靠的螺丝钉。