Mock
Mock
目录
- Mock测试简介
- 安装与准备
- 核心概念:Mock和patch
- 基础用法
- 创建Mock对象
- 验证方法调用
- 设置返回值与副作用
- 实战示例
- 模拟HTTP请求
- 模拟数据库操作
- 高级技巧
- 使用
autospec
确保接口兼容性 - 使用
Spy
部分模拟对象
- 使用
- 常见问题与最佳实践
1. Mock测试简介
Mock测试用于隔离被测代码的依赖项,通过模拟(Mock)外部服务(如API、数据库等)的行为,确保测试专注于当前代码逻辑,而非外部系统的可靠性。核心优势包括:
- 加速测试:避免网络请求或复杂计算。
- 提高稳定性:防止因外部服务故障导致测试失败。
- 验证交互:确认代码是否按预期调用了依赖项。
2. 安装与准备
Python标准库已包含unittest.mock
,无需额外安装。若使用第三方框架(如pytest
),可安装插件:
1
pip install pytest
3. 核心概念
- Mock对象:模拟任意对象,可自定义属性和行为。
- patch:临时替换目标对象为Mock,支持装饰器或上下文管理器形式。
4. 基础用法
创建Mock对象
1
2
3
4
5
6
7
from unittest.mock import Mock
# 创建Mock对象
mock_obj = Mock()
# 调用Mock方法(不会报错)
mock_obj.some_method()
设置返回值与属性
1
2
3
4
5
6
7
# 设置方法返回值
mock_obj.calculate.return_value = 42
print(mock_obj.calculate()) # 输出 42
# 设置属性
mock_obj.name = "Test Object"
print(mock_obj.name) # 输出 "Test Object"
验证调用行为
1
2
3
4
5
6
# 验证方法是否被调用
mock_obj.some_method.assert_called_once()
# 检查调用参数
mock_obj.method(arg1, arg2)
mock_obj.method.assert_called_with(arg1, arg2)
使用side_effect
模拟异常或动态行为
1
2
3
4
5
6
7
# 抛出异常
mock_obj.divide.side_effect = ZeroDivisionError
# 动态返回值
mock_obj.get_status.side_effect = [200, 404]
print(mock_obj.get_status()) # 200
print(mock_obj.get_status()) # 404
5. 实战示例
示例1:模拟HTTP请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
from unittest.mock import patch
def fetch_data(url):
response = requests.get(url)
return response.json()
# 测试用例
@patch('requests.get')
def test_fetch_data(mock_get):
# 模拟返回数据
mock_response = Mock()
mock_response.json.return_value = {"key": "value"}
mock_get.return_value = mock_response
result = fetch_data("http://api.example.com/data")
assert result == {"key": "value"}
mock_get.assert_called_once_with("http://api.example.com/data")
示例2:模拟数据库操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from unittest.mock import Mock
class Database:
def connect(self):
pass # 实际会连接真实数据库
def get_user(db, user_id):
conn = db.connect()
# 假设这里执行查询操作
return {"id": user_id, "name": "Alice"}
def test_get_user():
# 创建模拟数据库对象
mock_db = Mock()
mock_db.connect.return_value = "connection_handle"
user = get_user(mock_db, 1)
assert user == {"id": 1, "name": "Alice"}
mock_db.connect.assert_called_once()
6. 高级技巧
使用autospec
确保接口一致性
1
2
3
4
5
6
7
from unittest.mock import create_autospec
real_class = SomeComplexClass
mock_spec = create_autospec(real_class)
# 错误的调用会报错(如方法不存在)
mock_spec.invalid_method() # AttributeError
使用Spy
部分模拟对象
1
2
3
4
5
6
7
8
9
10
11
from unittest.mock import MagicMock
class Calculator:
def add(self, a, b):
return a + b
calc = Calculator()
calc.add = MagicMock(wraps=calc.add) # 保留原方法,同时监控调用
calc.add(2, 3) # 返回 5
calc.add.assert_called_with(2, 3)
7. 常见问题与最佳实践
- 问题:忘记停止
patch
导致状态泄漏。- 解决:优先使用装饰器或上下文管理器。
- 问题:过度Mock导致测试与实现紧耦合。
- 建议:仅Mock外部依赖,保留业务逻辑的真实测试。
- 最佳实践:
- 为每个测试用例独立创建Mock。
- 使用
spec
或autospec
避免接口不匹配。
This post is licensed under CC BY 4.0 by the author.