【接口自动化】06-轻量级框架封装(完):日志监控、数据驱动及断言
一、前言上一期我们知道了统一接口请求的轻量级框架的封装那么我们轻量级框架封装还未结束我们本期继续介绍轻量级框架封装即日志监控、数据驱动以及断言等等二、轻量级框架封装日志监控封装日志在开发中是很重要的同时在自动化测试中也很重要因为日志它可以记录程序在运行的时候发生的一些细节信息从而帮助我们了解程序的运行时过程进一步帮助我们判断排除程序出现的问题。在Python中进行日志记录有两种内置标准库logging【常用】第三方库loggru我们会选择内置的标准库logging因为他能够和pytest很好的集成。1、在pytest中启用日志记录如何启用呢将下述的配置内容放入到我们的pytest.ini配置文件中# 日志相关配置 # 日志文件存储路径相对项目根目录日志会输出到logs文件夹下的frame.log文件log_file./logs/frame.log# 日志输出级别低于该级别的日志不会写入文件# 级别优先级从小到大DEBUG调试 INFO信息 WARNING警告 ERROR错误 CRITICAL严重故障log_file_levelINFO# 日志打印格式模板# %(asctime)s日志打印的时间# %(levelname)s日志级别名称INFO/WARNING等# %(message)s自定义输出的日志内容log_file_format%(asctime)s %(levelname)s %(message)s解释日志格式字符串占位符标识作用说明%(asctime)s打印日志记录的时间%(levelname)s打印日志级别文字DEBUG/INFO/WARNING等%(message)s打印自定义输出的日志/报错信息主体%(levelno)s打印日志级别对应的数字编号%(pathname)s打印当前执行文件完整路径等价于sys.argv[0]%(filename)s打印当前执行的程序文件名%(funcName)s打印输出日志所在的函数名%(lineno)d打印打印日志代码所在的行号%(thread)d打印当前线程ID数字%(threadName)s打印当前线程名称%(process)d打印当前进程ID数字上述我们只是将日志给启用但是日志里面的内容我们需要一个日志对象来进行封装。2、创建日志对象logger使用logging.getLogger(名字)来创建# 导入日志模块的包importlogging# 创建日志对象loggerlogging.getLogger(__name__)# 使用__name__可以自动生成名字loggerlogging.getLogger(my_logger)# 自己指定名字3、使用logger来记录日志用法如下所示# 导入日志模块的包importlogging# 创建日志对象loggerlogging.getLogger(__name__)# 使用__name__可以自动生成名字# 开始记录日志logger.debug()# 记录一个debug级别的日志logger.info()# 记录一个info级别的日志logger.error()# 记录一个error级别的日志注意日志的内容必须是字符串如果做到字符串我们使用一个占位符然后将数据填充进去最终我们的数据也变为字符串了# 这个输出格式我们在Python语法基础中也有介绍过的data{1,2,3}logger.error(f数据的内容{data})4、给接口打上日志记录上一期我们对接口请求进行了轻量级的统一封装importrequestsclassRequestUtil:# 首先将session做为类对象供给大家共用sessrequests.session()# 创建一个参数 用于共用公共参数params{application:app,application_client_type:android,}# 创建一个方法构造请求defsend_request(self,**kwargs):# 使用不定长的关键字参数# 完善动态参数ifparamsinkwargs:# 不存在就添加存在就是修改kwargs[params].update(self.params)# 发送请求respself.sess.request(**kwargs)returnresp那么现在我们就需要给这个所封装的接口请求打上我们的日志便于我们的观察我们需要打上哪一些日志呢前置# 导入日志框架importlogging# 创建出日志对象loggerlogging.getLogger(__name__)1、请求前# 1、打印请求入参日志请求前 logger.info(f【发起HTTP请求】原始请求参数:{kwargs})2、记录请求# 2、打印请求日志logger.info(f【请求参数合并完成】最终请求params:{kwargs.get(params)})3、记录响应# 3、打印响应日志请求后 logger.info(f【接口响应结果】状态码{resp.status_code}, 响应头{dict(resp.headers)})try:# 优先打印json格式响应体resp_jsonresp.json()# 因为只有你的响应内容是json字符串那么才可以使用json()方法logger.info(f【响应JSON数据】{resp_json})exceptException:# 非json接口打印原始文本logger.info(f【响应文本数据】{resp.text})完整代码importrequests# 导入日志框架importlogging# 创建出日志对象loggerlogging.getLogger(__name__)classRequestUtil:# 首先将session做为类对象供给大家共用sessrequests.session()# 创建一个参数 用于共用公共参数params{application:app,application_client_type:android,}# 创建一个方法构造请求defsend_request(self,**kwargs):# 使用不定长的关键字参数# 1、打印请求入参日志请求前logger.info(f【发起HTTP请求】原始请求参数{kwargs})# 完善动态参数ifparamsinkwargs:# 不存在就添加存在就是修改kwargs[params].update(self.params)# 2、打印请求日志logger.info(f【请求参数合并完成】最终请求params:{kwargs.get(params)})# 发送请求respself.sess.request(**kwargs)# 3、打印响应日志请求后 logger.info(f【接口响应结果】状态码{resp.status_code}, 响应头{dict(resp.headers)})try:# 优先打印json格式响应体resp_jsonresp.json()# 因为只有你的响应内容是json字符串那么才可以使用json()方法logger.info(f【响应JSON数据】{resp_json})exceptException:# 非json接口打印原始文本logger.info(f【响应文本数据】{resp.text})returnresp结果我现在day05这个目录下运行那么我们就会在当前目录下创建一个logs文件夹打开里面有一个frame.log文件梳理逻辑上述完成日志的封装之后我们接下来就实现数据驱动测试的封装三、轻量级框架封装数据驱动封装我们需要实现使用数据文件去驱动测试用例也就是代码和数据文件的分离。具体的实现思路设计YAML数据文件格式加载YAML完成数据驱动测试1、设计YAML数据文件格式数据文件名的格式需要和测试用例名的格式一致唯一的区别就是测试用例名的test_改为ddt_什么意思呢比如你的一个测试用用例是test_tan那么现在你这个测试用例所对应的YMAL文件命名为ddt_tan这样一来你一看都是tan那么你就可以知道哪一个测试用例对应了哪一个数据驱动文件。如下那么我们在数据驱动文件中写什么呢上述我们的数据文件中定制的有两个内容一个是Allure报告的定制另一个是接口请求的内容-feature:首页模块story:首页接口title:首页接口请求成功request:method:geturl:http://116.62.63.211/shop/api.phpparams:s:index/index2、加载YAML完成数据驱动测试怎么去读取数据呢就看下面的图解你要去读取数据那么你就得让一个东西来帮助我们这个东西其实就是一个标记pytest.mark.parametrizepytest.mark.parametrize(变量名,打开文件并读取)def用例():代码如下importallureimportpytestfrom commons.request_utilimportRequestUtilfrom commons.yml_utilimportYmlUtilpytest.mark.parametrize(case_info,YmlUtil(rE:\python_project\basic-python-grammar-learning\api_framwor\day06\ddt_tan.yaml).read())deftest_tan(case_info):# 读取数据文件中的内容 featurecase_info[feature]storycase_info[story]titlecase_info[title]requestcase_info[request]# 此处定制Allure报告 allure.dynamic.feature(feature)allure.dynamic.story(story)allure.dynamic.title(title)respRequestUtil().send_request(**request)# 请求完毕之后他会有返回值那么此时我们就可以断言assertresp.json()[code]0结果打开YAML文件中的文件地址可以动态获取Path(__file__)获取当前文件的路径str(Path(__file__).parent)得到当前文件所在的目录变动如下图所示代码importallureimportpytestfrom pathlibimportPathfrom commons.request_utilimportRequestUtilfrom commons.yml_utilimportYmlUtilcase_pathstr(Path(__file__).parent)pytest.mark.parametrize(case_info,YmlUtil(case_path/ddt_tan.yaml).read())deftest_tan(case_info):# 读取数据文件中的内容 featurecase_info[feature]storycase_info[story]titlecase_info[title]requestcase_info[request]# 此处定制Allure报告 allure.dynamic.feature(feature)allure.dynamic.story(story)allure.dynamic.title(title)respRequestUtil().send_request(**request)# 请求完毕之后他会有返回值那么此时我们就可以断言assertresp.json()[code]0结果四、轻量级框架封装JSON断言的封装我们会使用一种工具通过这个工具来对JSON对象进行一些深度的比较1、先安装对应的模块pip install deepdiff2、简单演示怎么使用我们假设有两个字典(JSON在Python中叫做字典)dict1{a:1,b:2,c:{d:3}}dict2{a:1,b:4,e:5}那么有以下的代码fromdeepdiffimportDeepDiff# 从deepdiff(从小写中)中导入DeepDiff大写dict1{a:1,b:2,c:{d:3}}dict2{a:1,b:4,e:5}diffDeepDiff(dict1,dict2)assertdiff{},diff# 如果是一样的话那么就断言成功了 后面的diff是为了打印详细信息3、deepdiff的特点1深度比较递归的比较有嵌套的数据结构2检测多种差异添加或者删除内容、值的变化、类型的变化3相等断言当两个数据一致(没有差异)时候返回的是空值可以使用结果{}进行断言。五、案例实战本实战中使用的接口文档是我们之前用的微信公众平台的接口文档老铁们可参考Jmeter接口实战1、基本样式创建test_weixin.py在里面写两个方法一个是用来获取鉴权码一个是用来上传文件的fromcommons.request_utilimportRequestUtil# 导入请求工具类deftest_get_token():passdeftest_upload():pass2、构造获取鉴权码的请求要素然后发送请求进行断言提取鉴权码get_ver{}# 全局变量deftest_get_token():methodGETurlhttps://api.weixin.qq.com/cgi-bin/tokenparams{grant_type:client_credential,appid:wx9335efa56e8cbb64,secret:0750c9411423c510fd0c2a1acdc32aa4}# 发送请求先实例化再掉里面的方法发送请求resRequestUtil().send_request(methodmethod,urlurl,paramsparams)# 断言expires_inres.json()[expires_in]assertexpires_in7200# 提取鉴权码access_tokenres.json()[access_token]get_ver[access_token]access_token3、我想只执行这个方法所以我就加一个标记pytest.mark.weixin此时需要在我们的配置文件中加入一个标记测试通过4、接下来就完成上传文件的这个接口那么将上述的获取鉴权码的接口复制一下进行修改即可因为大部分都是相似的得到的代码如下fromcommons.request_utilimportRequestUtil# 导入请求工具类importpytest get_ver{}# 全局变量pytest.mark.weixindeftest_get_token():methodGETurlhttps://api.weixin.qq.com/cgi-bin/tokenparams{grant_type:client_credential,appid:wx9335efa56e8cbb64,secret:0750c9411423c510fd0c2a1acdc32aa4}# 发送请求先实例化再掉里面的方法发送请求resRequestUtil().send_request(methodmethod,urlurl,paramsparams)# 断言expires_inres.json()[expires_in]assertexpires_in7200# 提取鉴权码access_tokenres.json()[access_token]get_ver[access_token]access_tokenpytest.mark.weixindeftest_upload():methodPOSTurlhttps://api.weixin.qq.com/cgi-bin/media/uploadimgparams{access_token:get_ver[access_token],}# 参数是文件files{media:open(rE:\python_project\basic-python-grammar-learning\api_framwor\data\弹简特头像.jpg,rb)# 文件的上传必须使用二进制的方式此处的路径我们可以使用之前的相对路径}# 发送请求先实例化再掉里面的方法发送请求resRequestUtil().send_request(methodmethod,urlurl,paramsparams,filesfiles)# 断言urlres.json()[url]asserthttpinurl结果通过六、总结接口自动化轻量级封装到目前我们轻量级的封装就暂时告一段落了我们接下来就总结一下我们的所学1、接口自动化测试的流程理解需求设计用例根据用例需求查找接口文档根据接口文档编写测试代码测试代码中包含请求的参数和预期的结果2、接口自动化测试中接口关联和数据驱动怎么做的接口关联先通过正则(字符串)或者JSONPATH(JSON)从上一个接口中提取数据把数据保存到全局变量中在下一个接口中使用变量即可完成关联数据驱动测试分析接口参数中会发生变化的内容不会变的东西不要用数据驱动将变化的部分写入yaml文件中因为YAML文件更便于阅读然后我们再编写代码从YAML文件中读取数据使用pytest的参数化标记进行数据驱动3、你们断言是怎么做的文本的响应类型判断包含或者相等实际上就是字符串之间的比较JSON的响应类型方法一使用JSONPATH提取目标数据对目标数据进行断言方法二DeepDiff对响应的内容进行断言4、你们的接口测试框架是怎么封装的首先我们要对技术栈有一个了解pytestrequestjsonpathallurelogging使用pytest作为一个基础的框架集成一些第三方插件对request进行一些统一请求的封装目的是进行公共参数的管理和日志的记录为了更方便的从json响应中提取数据进行管理和断言封装jsonpath利用allure进行测试报告的定制用例分组、自定义logo等等

相关新闻

90后35岁时我决定从运维转行:找一门越老越吃香的职业!

90后35岁时我决定从运维转行:找一门越老越吃香的职业!

90 后 35岁时我决定从运维转行:找一门越老越吃香的职业! 35岁之前,我总认为运维这个职位只要能熬得住、愿意加班的话,就一定能够熬出头来。但是真正做了几年之后才明白过来,巡检、处理报警、半夜救火等事情已经成为了…

2026/10/9 0:49:51 阅读更多 →
油田通信维护进入集成时代|鼎讯 RM-1000 与一线运维模式探索

油田通信维护进入集成时代|鼎讯 RM-1000 与一线运维模式探索

在石油行业,通信维护是一项 “看不见” 的工作,却直接影响着生产的 “看得见” 的效率。 从钻井平台到输油管道,从炼化基地到储油库区,每一个环节的通信设备都需要定期检测和维护。过去,这项工作依赖多个独立仪器配合完…

2026/10/5 11:52:41 阅读更多 →
智能体测开Day4

智能体测开Day4

昨日回顾今日讲解vi 编辑文件命令搜索命令替换命令创建session连接别人的Linux1.首先找到vmware中编辑->虚拟网络编辑器->选择仅主机点击更多设置->选择NAT模式->选择NAT设置->主机端口选择一个未被占用的端口号->虚拟机IP地址查找CentOS的IP->虚拟机端口…

2026/10/13 17:38:08 阅读更多 →

最新新闻

Fontsource 字体解析:Amarante —— 基于 Art Nouveau 的中等对比度展示字体

Fontsource 字体解析:Amarante —— 基于 Art Nouveau 的中等对比度展示字体

前端CLI开发工具 【免费下载链接】fontsource Self-host Open Source fonts in neatly bundled NPM packages. 项目地址: https://gitcode.com/gh_mirrors/fo/fontsource 点击查看 免费下载 Amarante 是 Fontsource 注册表中收录的 Google 系字体之一,由…

2026/10/15 5:39:22 阅读更多 →
372.高通 EDL Sahara/Firehose 协议详解,安卓黑砖救砖 + 分区镜像校验实战源码

372.高通 EDL Sahara/Firehose 协议详解,安卓黑砖救砖 + 分区镜像校验实战源码

摘要 本文面向具备基础计算机操作能力的开发者与维修人员,系统性地阐述安卓设备刷机与维修的底层逻辑。文章从BootROM到Android系统的启动链路出发,深入解析Fastboot与EDL两种核心刷机协议的通信机制,结合真实变砖救援案例,提供完整的操作流程与可运行的Python自动化脚本。…

2026/10/15 5:39:22 阅读更多 →
直流偏磁下变压器励磁电流谐波分析:Simulink仿真与FFT指南

直流偏磁下变压器励磁电流谐波分析:Simulink仿真与FFT指南

简介:这份文档是重庆邮电大学自动化学院/工业互联网学院学生在“复杂电气工程系统设计”课程中完成的A等实验报告,主题聚焦直流偏磁下变压器励磁电流谐波分析。报告从地磁风暴与高压直流输电两大现实场景切入,系统阐述直流偏磁的产生机理、对…

2026/10/15 5:39:22 阅读更多 →
基于SSM的门面商铺租赁管理系统设计与实现

基于SSM的门面商铺租赁管理系统设计与实现

每年到这个时间点,总有准备毕设的同学来问:JavaWeb方向到底选什么题目,既能体现技术功底,又不至于把自己逼到绝路。如果你正在“基于SSM的门面商铺租赁管理系统”这个题目上犹豫,我直接说结论:这是一个非常…

2026/10/15 5:39:22 阅读更多 →
MutationObserver:高效监控DOM变化的现代API

MutationObserver:高效监控DOM变化的现代API

1. MutationObserver 基础解析MutationObserver 是现代浏览器提供的原生API,用于监测DOM树中的节点变化。与传统的Mutation Events相比,它具有显著的性能优势:异步触发机制避免频繁回调导致的性能问题批量处理DOM变更,减少不必要的…

2026/10/15 5:39:22 阅读更多 →
Multi-Agent系统Tracing实战:从线上事故到执行链记录体系

Multi-Agent系统Tracing实战:从线上事故到执行链记录体系

1. 从一次线上事故说起:为什么Multi-Agent系统必须做Tracing去年冬天,我们团队上线了一套基于多智能体协作的自动化内容处理系统,四个Agent分别负责信息抽取、逻辑校验、格式转换和最终输出。上线第三天,运营同学反馈说有一批文档…

2026/10/15 5:38:22 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →