3、安装与引入unittest.mock库:Python标准库介绍
unittest.mock 是 Python 标准库的一部分。没错,从 Python 3.3 开始它就内置了。
我刚开始写测试时,以为要 pip install mock,折腾了半天……后来才发现,原来早就装好了。
这一节咱们就把它捋清楚:怎么引入、有什么注意事项、怎么验证。
3.1 它到底在哪?
unittest.mock 位于 Python 的标准库套件中,属于 unittest 模块的子模块。你不需要额外安装任何包。
简单来说:
- Python 3.3+:直接用
from unittest.mock import ... - Python 2.x / 3.2 及更低:需要
pip install mock,然后from mock import ... - Python 3.x 最新版(3.8+):依然保持标准库状态,并且增加了不少实用特性
重要结论:如果你在用 Python 3.3 及以上,不需要装任何东西。直接引入即可。
3.2 如何引入?常见写法
我个人习惯这样写:
from unittest.mock import Mock, MagicMock, patch, PropertyMock
这就够了。你可能会问:为什么要同时引入 Mock 和 MagicMock?
嗯,简单回答:MagicMock 是 Mock 的子类,它自动帮你实现了 __len__、__iter__ 等魔术方法。大部分场景用 MagicMock 更省事。
如果你只需要一个简单的替身对象,也经常这么写:
from unittest.mock import Mock
m = Mock()
m.some_method.return_value = 42
还有一种常见的写法:
import unittest.mock
# 然后通过 unittest.mock.Mock 访问
但这样的代码会显得啰嗦。我建议用 from ... import 的方式,更清爽。
3.3 版本差异:小心这个坑
我曾经在写一个跨 Python 2 和 3 的项目时,直接用了 from unittest.mock import patch,结果在 Python 2 的 CI 上直接报 ImportError。排查半天才发现是版本兼容问题。
解决方案很简单:
- 如果只支持 Python 3.3+,直接
from unittest.mock import ... - 如果需要兼容 Python 2 / 3.2 以下,用
mock第三方库,并统一引入方式:
try:
from unittest.mock import Mock, patch
except ImportError:
from mock import Mock, patch
另外,Python 3.5 之前 AsyncMock 还不存在(AsyncMock 是 3.8 加入的)。如果你要 mock 异步代码,注意版本。
⚠️ 版本注意:AsyncMock 在 Python 3.8+ 才可用。Python 3.7 及以下需要手动 mock 协程或使用第三方库。
3.4 验证是否已安装
打开终端,输入一行命令就能确认:
python -c "from unittest.mock import Mock; print('OK')"
如果不报错,输出 OK,那就说明一切正常。
如果你用的是老旧 Python(比如 2.7 或 3.2),想要用 mock 标准库,那只能升级 Python 了。或者安装第三方包:
pip install mock
然后改用 from mock import Mock。
3.5 引入后能做什么?——核心对象一览
下面这张表展示了我最常用的几个对象,以及它们各自的用途:
| 对象 | 用途 | 什么时候用 |
|---|---|---|
Mock | 基础替身对象 | 大部分场景的默认选择 |
MagicMock | 带魔术方法的替身 | 需要支持 len()、iter() 等操作时 |
patch | 上下文管理器 / 装饰器,临时替换目标对象 | 需要替换外部依赖时(推荐方式) |
PropertyMock | 模拟属性(property) | 替身需要返回动态属性值 |
AsyncMock | 模拟异步函数 | Python 3.8+,测试 async 代码 |
你想想看,这五个对象几乎覆盖了 95% 的 mock 需求。后面章节我会详细摆弄它们。
3.6 本章知识结构
下面这张图帮你理清 unittest.mock 的定位和脉络:
3.7 避坑指南 & 个人建议
- 不要混用引入路径:一会
from unittest.mock import Mock,一会from mock import Mock,同一个测试文件里会报错。统一引入写法。 - 注意 modules 替换:如果你用
patch时引入路径写错了(比如漏了完整包名),替身根本不会生效。我踩过这个坑:patch('os.path.exists')写成了patch('path.exists'),排查了很久。 - Mock 对象不是万能的:它只能替换你显式指定的调用。如果外部代码通过
__import__动态导入,patch可能覆盖不到。记得先测一下。
💡 小技巧:写完测试后,运行 python -m pytest --tb=short,如果遇到 ModuleNotFoundError: No module named 'unittest.mock',那表明你的 Python 版本低于 3.3。别慌,用 pip install mock 然后改导入方式就好了。