当前位置: 首页 > news >正文

python单元测试 unittest.mock.patch (二)

unittest.mock.patch 的核心能力是**“临时替换对象行为”,除了模拟异常,它在测试中还有很多高频应用场景,本质都是为了隔离外部依赖、控制测试环境、验证代码行为**。以下是最常用的几类场景,结合具体例子说明:

一、替换函数返回值(最基础用法)

当测试代码依赖某个“不好控制返回值”的函数(比如调用外部API、读取数据库、随机数生成)时,用 patch 强制让它返回固定值,避免测试结果受外部因素影响。

例子:测试依赖随机数的函数

假设我们有一个函数 is_lucky(),它调用 random.random() 生成随机数,若大于0.5则返回“幸运”,否则返回“倒霉”。
直接测试时,结果是随机的(不稳定),用 patch 替换 random.random() 的返回值,就能稳定测试两种情况。

import random
from unittest.mock import patch# 目标函数:依赖随机数
def is_lucky():num = random.random()  # 生成0-1的随机数return "幸运" if num > 0.5 else "倒霉"# 测试1:模拟random.random()返回0.6(>0.5)
with patch("random.random", return_value=0.6):assert is_lucky() == "幸运"  # 必然通过# 测试2:模拟random.random()返回0.4(<0.5)
with patch("random.random", return_value=0.4):assert is_lucky() == "倒霉"  # 必然通过
  • 关键参数:return_value=固定值 用于指定被替换函数的返回结果,替代原函数的逻辑。

二、验证函数是否被正确调用(调用追踪)

测试中常需要确认:某个函数是否被调用、被调用了几次、传入的参数是否正确。patch 生成的“模拟对象”自带追踪能力,可通过 assert_called_* 系列方法验证。

例子:测试通知函数的调用逻辑

假设我们有一个 send_gift(user) 函数,当用户积分>100时,会调用 notify(user, "送礼物") 发送通知。
我们需要测试:当积分足够时,notify 是否被正确调用。

# 目标函数:根据积分决定是否发送通知
def notify(user, msg):print(f"通知{user}{msg}")  # 实际可能是发送短信/邮件def send_gift(user, points):if points > 100:notify(user, "送你一份礼物!")  # 满足条件时调用notify# 测试:验证积分>100时,notify被正确调用
from unittest.mock import patchdef test_send_gift():# 用patch替换notify,生成一个“模拟版notify”(mock_notify)with patch("__main__.notify") as mock_notify:# 调用send_gift,传入满足条件的参数send_gift("张三", 150)# 验证notify被调用了1次mock_notify.assert_called_once()# 验证调用时的参数是("张三", "送你一份礼物!")mock_notify.assert_called_with("张三", "送你一份礼物!")test_send_gift()  # 测试通过,说明调用逻辑正确
  • 核心逻辑:patch(...) as mock_obj 会将模拟对象赋值给 mock_obj,通过 mock_obj 的方法(如 assert_called_once()assert_called_with(...))验证调用情况。

三、模拟外部依赖(文件、网络、数据库)

测试中若涉及外部资源(如读文件、发HTTP请求、查数据库),直接调用会导致测试不稳定(比如文件不存在、网络波动)。用 patch 替换这些外部依赖的接口,可彻底隔离环境。

例子:测试读取文件的函数

假设我们有一个 read_first_line(file_path) 函数,用于读取文件第一行。直接测试需要真实文件,用 patch 模拟 open 函数,避免依赖真实文件。

# 目标函数:读取文件第一行
def read_first_line(file_path):with open(file_path, "r") as f:return f.readline().strip()# 测试:用patch模拟open函数,返回预设内容
from unittest.mock import patch, mock_opendef test_read_first_line():# 模拟文件内容:第一行是"hello world"mock_file_content = "hello world\nsecond line"# 用mock_open创建一个模拟的open函数,返回上述内容mock_file = mock_open(read_data=mock_file_content)# 用patch替换内置的open函数,指向mock_filewith patch("builtins.open", mock_file):# 调用目标函数(此时open是模拟的,不依赖真实文件)result = read_first_line("任意路径.txt")# 验证结果是否正确assert result == "hello world"# 验证open是否以"r"模式打开了目标路径mock_file.assert_called_with("任意路径.txt", "r")test_read_first_line()  # 测试通过,不依赖真实文件
  • 扩展:类似地,可模拟 requests.get 来测试网络请求逻辑(比如模拟API返回的JSON数据),无需真实发请求。

四、替换类的实例或方法

当测试涉及复杂类的实例(比如数据库连接类、第三方工具类)时,用 patch 替换类的方法或直接替换实例,避免创建真实实例的开销或复杂性。

例子:测试依赖数据库连接的函数

假设 get_user_name(db_conn, user_id) 函数需要通过数据库连接 db_conn 查询用户名。我们用 patch 模拟 db_connquery 方法,返回固定结果。

# 目标函数:通过数据库连接查询用户名
def get_user_name(db_conn, user_id):result = db_conn.query(f"SELECT name FROM users WHERE id={user_id}")return result[0]["name"] if result else "未知用户"# 测试:模拟数据库连接的query方法
from unittest.mock import Mock, patchdef test_get_user_name():# 创建一个模拟的数据库连接对象(Mock实例)mock_db_conn = Mock()# 设定模拟的query方法返回值:当查询id=1时,返回[{"name": "张三"}]mock_db_conn.query.return_value = [{"name": "张三"}]# 调用目标函数(传入模拟的db_conn)result = get_user_name(mock_db_conn, 1)# 验证结果assert result == "张三"# 验证query是否被传入了正确的SQLmock_db_conn.query.assert_called_with("SELECT name FROM users WHERE id=1")test_get_user_name()  # 测试通过,无需真实数据库
  • 这里没有用 patch 语句,而是直接创建 Mock 对象(patch 本质也是生成 Mock 对象),适合替换类实例的场景。

五、总结:patch 的核心应用场景

应用场景核心目的关键参数/方法
模拟异常验证错误处理逻辑side_effect=异常对象
替换返回值控制依赖函数的输出,稳定测试结果return_value=固定值
验证调用情况确认函数被正确调用(次数、参数)assert_called_* 系列方法
模拟外部依赖隔离文件、网络、数据库等不稳定资源替换 open/requests.get
替换类实例/方法简化复杂类的测试,避免真实实例开销结合 Mock 对象使用

这些场景的本质都是**“用可控的模拟逻辑替代不可控的真实逻辑”**,让测试更稳定、更快、更易复现。无论是单元测试还是集成测试,patch 都是隔离依赖、验证行为的核心工具。

http://www.dtcms.com/a/488118.html

相关文章:

  • 手机网站后台编辑器有哪些贵州建筑网站
  • 如果使用自己电脑做网站com是什么网站
  • 泛微 企业网站建设计划网站出现 503怎么了
  • 兰州移动端网站建设如何做好网站站内优化
  • PS基本教学(二)——认识PS软件各个基础模块以及PS基本设置
  • 大模型Agent智能体:开启人工智能新时代
  • 常备资料查询
  • 20251015给荣品的PRO-RK3566开发板在buildroot下打开ov5645【只配置编译了】
  • 淄博网站排名公司苏州网页关键词优化
  • 网站设置密码访问一建二建报考条件及专业要求
  • 青岛市住房城乡建设厅网站php网站建设个人总结
  • 濮阳网站关键词网站做下载wordpress
  • 上海站优云网络科技有限公司简单的网站怎么做
  • django网站开发实例pdfseo交流群
  • 重庆建网站搜索快忻科技html代码注释
  • 如何做公司o2o网站网站制作杭州
  • Python自定义容器完全指南:从基础实现到高级模式
  • 小程序做网站济南网站建设方案咨询
  • 介绍近期github上有名的开源项目
  • 相应式手机网站建设网站可不可以做自己的专利
  • 网站建设菜鸟教程模板建站合同
  • PyQt5 串口上位机开发笔记:如何给界面更换图标
  • 响应式购物网站模板不想让网站保存密码怎么做
  • C#:函数默认参数
  • 比较指令 CMP 解析
  • 做设计接私活的网站优化近义词
  • 苏州知名网站制作设计保障性租赁住房管理平台
  • 今夕窗口批量启动排序以及窗口大小调整工具软件
  • 共建智能视觉生态,Deepano(嘀拍科技)授权世强硬创平台代理
  • HarmonyOS 用 attributeModifier 修改按钮背景但按压态不生效