
Python 的 mock 主要用于在单元测试中隔离外部依赖,比如数据库、网络请求、第三方 API 或复杂计算逻辑。核心是用可控的“假对象”替代真实对象,让测试专注验证自身逻辑,而不是被外部环境干扰。
用 patch 临时替换对象(最常用)
当你要模拟模块里某个函数、类或属性时,patch 是首选。它能在测试运行期间动态替换目标,测试结束自动还原,避免污染其他测试。
- 装饰器写法(推荐,清晰易读):
from unittest.mock import patch
import requests
@patch('requests.get')
def test_fetch_data(mock_get):
mock_get.return_value.json.return_value = {'id': 1, 'name': 'test'}
result = fetch_user(1)
assert result['name'] == 'test'
mock_get.assert_called_once_with('https://api.example.com/users/1')
- 上下文管理器写法(适合单个测试块内局部控制):
with patch('builtins.open', mock_open(read_data='hello')) as mock_file:
content = read_config()
assert content == 'hello'
立即学习“Python免费学习笔记(深入)”;
⚠️ 注意:patch 的路径必须是“被测试代码里导入并使用的位置”,不是定义位置。例如你在 my_module.py 中写了 import requests,然后调用 requests.get(),那就要 @patch('my_module.requests.get') —— 很多人在这里出错。
控制返回值和行为:return_value vs side_effect
return_value 适用于固定返回;side_effect 更灵活,支持抛异常、动态返回、甚至调用真实函数。
-
return_value=42→ 每次调用都返回 42 -
return_value={'status': 'ok'}→ 返回字典,可链式调用(如mock_obj.data.name) -
side_effect=ValueError('timeout')→ 调用即抛异常 -
side_effect=[1, 2, 3]→ 第一次调用返回 1,第二次 2,第三次 3 -
side_effect=lambda x: x * 2→ 接收参数,返回计算结果
模拟类实例与方法调用
想测试一个类的方法是否被正确调用,或者验证它如何与依赖交互?可以 mock 整个类,再配置它的实例行为。
from unittest.mock import patch, MagicMock
@patch('my_module.Database')
def test_save_user(mock_db_class):
# mock_db_class() 返回一个 mock 实例
mock_db = mock_db_class.return_value
mock_db.save.return_value = True
result = save_user_to_db({'name': 'Alice'})
assert result is True
mock_db.save.assert_called_once_with({'name': 'Alice'})
关键点:mock_db_class.return_value 是对“实例”的模拟,而 mock_db.save 是对“实例方法”的模拟。别混淆 return_value 的层级。
检查调用情况:断言真实发生了什么
光有返回值不够,还要确认你的代码确实按预期调用了依赖。mock 对象自带丰富的断言方法:
-
assert_called()—— 至少调用过一次 -
assert_called_once()—— 精确调用一次 -
assert_called_with(arg1, arg2)—— 最后一次调用的参数匹配 -
assert_has_calls([call(1), call(2)])—— 检查多次调用的完整顺序 -
call_args和call_args_list—— 获取实际传入的参数(可用于调试)
例如:assert mock_send.call_args_list == [call('hi'), call('bye')] 可精准验证调用序列。










