第3章 Mock技术入门:概念、作用与基本用法
嗯,咱们直接进入正题。上一章聊了聊状态栏逻辑验证的痛点,那会儿我就在想——如果有办法把那些外部依赖(后端接口、硬件状态、时间变化)全都“替换”成我能控制的东西,测试不就简单多了吗?这就是Mock要干的事。
3.1 为什么需要Mock?
我刚开始做状态栏测试时,每次跑用例都要连真实后端。后端一挂,所有用例全红。更麻烦的是,有些场景(比如电量1%低电量警告)你得等手机真的耗到1%才能触发,这谁能忍?
说白了,真实依赖有三大问题:
- 不可控:你没法让后端在2秒内恰好返回超时
- 速度慢:网络IO、硬件读取,一次就要几百毫秒
- 副作用:真写数据库?真发短信?测试跑完数据全乱了
所以我们需要一个“替身”,替它干活,还能听我们指挥。这就是Mock。
3.2 什么是Mock?
Mock,直译是“模拟”。在测试里,它是一个假对象,行为完全由你定义。你告诉它:“当调用getStatus时,返回{battery: 80}”。它就这么干,不折不扣。
它跟Stub的区别?嗯,顺手提一下。Stub只返回固定数据,而Mock还记录调用过程——你调了没?调了几次?参数对不对?这些都能查。我习惯把Mock看成“带录像功能的Stub”。
我的理解:Mock就像一个训练有素的演员。你可以给它剧本(返回值),它还能告诉你“刚才观众笑了几次”(调用验证)。
3.3 Mock的核心作用
结合状态栏这个场景,我们总结一下Mock的三大作用:
- 隔离依赖:把被测的状态栏逻辑和后端API、传感器、系统时间全部解耦。只测逻辑本身。
- 加速执行:Mock返回数据不用等网络,毫秒级完成。我印象很深,有一次把200个状态栏用例从15分钟压到30秒。
- 覆盖边界:用Mock可以轻松模拟“后端返回-1”、“电池传感器烧糊了”、“时间跳变到2038年”等奇葩场景。这些在真实环境中几乎不可能触发。
3.4 基本用法:一个简短的例子
Python自带的unittest.mock库就够用了。来看一段代码:
from unittest.mock import Mock
# 创建Mock对象,模拟后端API
mock_api = Mock()
mock_api.get_status.return_value = {"battery": 80, "signal": 4}
# 状态栏逻辑(被测试的函数)
def display_status(api):
status = api.get_status()
return f"电量{status['battery']}% 信号{status['signal']}格"
# 测试
result = display_status(mock_api)
assert result == "电量80% 信号4格"
你看,整个测试不需要启动任何后端服务。更爽的是,你还能验证调用次数:
mock_api.get_status.assert_called_once()
如果逻辑里不该调第二次,这句就能帮你抓到bug。我在项目中就靠它发现过循环里重复拉数据的性能问题。
3.5 常见Mock框架对比
不同语言、不同生态都有Mock工具。我列一个自己用过的对比表:
| 框架/库 | 语言 | 特点 | 适用场景 |
|---|---|---|---|
| unittest.mock | Python | 内置,功能完整,支持spy | 绝大多数Python项目 |
| pytest-mock | Python | 基于pytest插件,写法更简洁 | 配合pytest的团队 |
| Mockito | Java/Python等 | 强类型,支持注解 | Java大型项目 |
| Moq | C# | 泛型强,配合LINQ | .NET生态 |
我个人偏爱pytest-mock,因为它和pytest的fixture结合得很自然。不过如果项目里没用pytest,那就老老实实用unittest.mock,也很稳。
3.6 避坑指南
我曾经踩过的坑:
- Mock了不该Mock的类内部方法,导致测试永远绿但线上出bug。记住:只Mock外部依赖,被测单元自身的方法不要Mock。
- 忘记配置
spec参数,Mock变成了“黑洞”——任何属性、任何方法调用都不会报错。这会让测试失去保护作用。我建议始终传入spec=真实类或autospec=True。 - 过度Mock:把几行逻辑里的所有函数全Mock了,最后测试变成了“测试Mock本身”,毫无意义。
3.7 知识结构总结
下面这张图梳理了Mock在整个测试中的位置和核心要素。建议你看完后再回头看代码,会更通透。
关键要点回顾:
- Mock的核心是替代真实依赖,让测试可控、快速、无副作用
- 选用库时优先考虑语言内置方案(如Python的
unittest.mock) - 配置
spec或autospec防止Mock变成无底黑洞 - 只Mock外部,不Mock内部逻辑