第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的三大作用:

  1. 隔离依赖:把被测的状态栏逻辑和后端API、传感器、系统时间全部解耦。只测逻辑本身。
  2. 加速执行:Mock返回数据不用等网络,毫秒级完成。我印象很深,有一次把200个状态栏用例从15分钟压到30秒。
  3. 覆盖边界:用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.mockPython内置,功能完整,支持spy绝大多数Python项目
pytest-mockPython基于pytest插件,写法更简洁配合pytest的团队
MockitoJava/Python等强类型,支持注解Java大型项目
MoqC#泛型强,配合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替换依赖关系图 被测单元(状态栏逻辑) 后端API 硬件传感器 系统时间 Mock API Mock Sensor Mock Time 测试用例 验证结果(断言) 虚线:原本依赖但被替换 实线:Mock替换后的调用路径 箭头方向表示数据流向

关键要点回顾:

  • Mock的核心是替代真实依赖,让测试可控、快速、无副作用
  • 选用库时优先考虑语言内置方案(如Python的unittest.mock)
  • 配置spec或autospec防止Mock变成无底黑洞
  • 只Mock外部,不Mock内部逻辑