apiAutoTest:自动化测试中的数据依赖处理
原始版本
简书:https://www.jianshu.com/p/6bfaca87a93b
博客园:https://www.cnblogs.com/zy7y/p/13426816.html
testerhome:https://testerhome.com/topics/25003
最新用例截图以及用例填写格式
数据依赖/路径参数依赖
我理解的参数依赖/接口依赖就是接口进行关联操作,比如有些查询接口需要登录之后才可以操作,那么我们就需要拿到token之类的东西,这一部分东西是放到header中的,apiAutoTest围绕的只有路径参数依赖,请求数据依赖
-
路径参数依赖
譬如说现在的restful,一个users接口,路由一般这样的
users
他的请求方式是get,这个路由我们把他认为是查所有用户,如果查某一个用户可能是这样的users/:id
也是个get请求,这里这个id想表达的意思是这里有个需要个用户id的参数,比如1-500里面的任意1个,也就是说这个id是可变的,可以从登录接口的返回响应取一个叫userId的值 -
请求参数依赖
这个应该好理解些,就是说支付接口需要的订单id,是从上一步提交订单接口返回的响应订单id
举个例子
假设现在有个实际响应结果字典如下
{"case_002": { "data": { "id": 500, "username": "admin", "mobile": "12345678", }}, "case_005": { "data": { "id": 511, "create_time": 1605711095 }, } }
-
excel中接口路径内容:
users/&$.case_005.data.id&/state/&$.case_005.data.careate_time&
代码内部解析后如下:
users/511/state/1605711095
&$.case_005.data.id&
代表从响应字典中提取case_005字典中data字典中的id的值,提取出来的结果是511 -
excel中请求参数内容如下:
{ "pagenum": 1, "pagesize": "12", "data": &$.case_005.data&, "userId": &$.case_002.data.id& } 代码内部解析后如下:
{ "pagenum": 1, "pagesize": "12", "data": { "id": 511, "create_time": 1605711095 }, "userId": 500 }
其实不难看出其中规则&jsonpath提取语法&
,如果你需要的内容是字符串类型,只需要这样"&jsonpath提取语法&"
上传文件
用例中书写格式,在上传文件栏
# 单文件上传在excel中写法 {"接口中接受文件对象的参数名": "文件路径地址"} # 多文件上传在excel中写法 {"接口中接受文件对象的参数名": ["文件路径1", "文件路径2"]}
预期结果
用例书写格式
# 断言一个内容 {"jsonpath提取表达式": 预期结果内容} # 多个断言 {"jsonpath提取表达式1": 预期结果内容1,"jsonpath提取表达式2": 预期结果内容2}
其他优化
- config.yaml文件中新增可配置初始header,整体代码优化,相比之前,同样测试用例执行下,快了2s左右
- 将配置文件读取,用例读取整合在
read_file.py
下 - 移除报告压缩方法
- 减少日志信息
现依赖处理代码
tools/init.py
#!/usr/bin/env/python3 # -*- coding:utf-8 -*- """ @project: apiAutoTest @author: zy7y @file: __init__.py @ide: PyCharm @time: 2020/7/31 """ import json import re import allure from jsonpath import jsonpath from loguru import logger def extractor(obj: dict, expr: str = '.') -> object: """ 根据表达式提取字典中的value,表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data :param obj :json/dict类型数据 :param expr: 表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data $.0.1 提取字典中的第一个列表中的第二个的值 """ try: result = jsonpath(obj, expr)[0] except Exception as e: logger.error(f'提取不到内容,丢给你一个错误!{e}') result = None return result def rep_expr(content: str, data: dict, expr: str = '&(.*?)&') -> str: """从请求参数的字符串中,使用正则的方法找出合适的字符串内容并进行替换 :param content: 原始的字符串内容 :param data: 在该项目中一般为响应字典,从字典取值出来 :param expr: 查找用的正则表达式 return content: 替换表达式后的字符串 """ for ctt in re.findall(expr, content): content = content.replace(f'&{ctt}&', str(extractor(data, ctt))) return content def convert_json(dict_str: str) -> dict: """ :param dict_str: 长得像字典的字符串 return json格式的内容 """ try: if 'None' in dict_str: dict_str = dict_str.replace('None', 'null') elif 'True' in dict_str: dict_str = dict_str.replace('True', 'true') elif 'False' in dict_str: dict_str = dict_str.replace('False', 'false') dict_str = json.loads(dict_str) except Exception as e: if 'null' in dict_str: dict_str = dict_str.replace('null', 'None') elif 'true' in dict_str: dict_str = dict_str.replace('true', 'True') elif 'False' in dict_str: dict_str = dict_str.replace('false', 'False') dict_str = eval(dict_str) logger.error(e) return dict_str def allure_title(title: str) -> None: """allure中显示的用例标题""" allure.dynamic.title(title) def allure_step(step: str, var: str) -> None: """ :param step: 步骤及附件名称 :param var: 附件内容 """ with allure.step(step): allure.attach(json.dumps(var, ensure_ascii=False, indent=4), step, allure.attachment_type.TEXT)
tools/data_process.py
#!/usr/bin/env/python3 # -*- coding:utf-8 -*- """ @project: apiAutoTest @author: zy7y @file: data_process.py @ide: PyCharm @time: 2020/11/18 """ from tools import logger, extractor, convert_json, rep_expr, allure_step from tools.read_file import ReadFile class DataProcess: response_dict = {} header = ReadFile.read_config('$.request_headers') have_token = header.copy() @classmethod def save_response(cls, key: str, value: object) -> None: """ 保存实际响应 :param key: 保存字典中的key,一般使用用例编号 :param value: 保存字典中的value,使用json响应 """ cls.response_dict[key] = value logger.info(f'添加key: {key}, 对应value: {value}') @classmethod def handle_path(cls, path_str: str) -> str: """路径参数处理 :param path_str: 带提取表达式的字符串 /&$.case_005.data.id&/state/&$.case_005.data.create_time& 上述内容表示,从响应字典中提取到case_005字典里data字典里id的值,假设是500,后面&$.case_005.data.create_time& 类似,最终提取结果 return /511/state/1605711095 """ # /&$.case.data.id&/state/&$.case_005.data.create_time& return rep_expr(path_str, cls.response_dict) @classmethod def handle_header(cls, token: str) -> dict: """处理header :param token: 写: 写入token到header中, 读: 使用带token的header, 空:使用不带token的header return """ if token == '读': return cls.have_token else: return cls.header @classmethod def handler_files(cls, file_obj: str) -> object: """file对象处理方法 :param file_obj: 上传文件使用,格式:接口中文件参数的名称:"文件路径地址"/["文件地址1", "文件地址2"] 实例- 单个文件: &file&D: """ if file_obj == '': return for k, v in convert_json(file_obj).items(): # 多文件上传 if isinstance(v, list): files = [] for path in v: files.append((k, (open(path, 'rb')))) else: # 单文件上传 files = {k: open(v, 'rb')} return files @classmethod def handle_data(cls, variable: str) -> dict: """请求数据处理 :param variable: 请求数据,传入的是可转换字典/json的字符串,其中可以包含变量表达式 return 处理之后的json/dict类型的字典数据 """ if variable == '': return data = rep_expr(variable, cls.response_dict) variable = convert_json(data) return variable @classmethod def assert_result(cls, response: dict, expect_str: str): """ 预期结果实际结果断言方法 :param response: 实际响应字典 :param expect_str: 预期响应内容,从excel中读取 return None """ expect_dict = convert_json(expect_str) index = 0 for k, v in expect_dict.items(): actual = extractor(response, k) index += 1 logger.info(f'第{index}个断言,实际结果:{actual} | 预期结果:{v} \n断言结果 {actual == v}') allure_step(f'第{index}个断言', f'实际结果:{actual} = 预期结果:{v}') assert actual == v
源码地址
master: 分支为最新代码
version1.0: 分支为之前开源的代码(通过字典迭代的方式来处理数据依赖)
Https://gitee.com/zy7y/apiAutoTest.git
Https://github.com/zy7y/apiAutoTest.git
后续打算
目前在公司正在做接口测试,说实话也是摸索着来,以上的优化项都是实际做的过程中突然想到的,然后就更新了
- 接入用例前后置SQL, 前置SQL目前想的是现在项目中遇到的问题,有些接口没有返回需要的数据,这里就要用前置SQL查询的结果传到请求数据里面了,后置SQL主要是请求后查看数据库中的数据是否变动,形成数据库断言 2020/12/08 完成 , 至此 apiAutoTest 应该不会 再有大更新~~~ 谢谢 看这个Demo的人
- 企业微信推送:目前项目中预想的效果,是后端人员提交代码,自动部署之后,通过gitlab-ci 启动测试代码,进行接口测试完成之后采集allure中的测试结果一有异常/失败用例就发送邮件并进行企业微信推送给领导 这个其实就是重新发送请求~
- .... 就不说了还有很多优化项,能力不够好好充电吧,~~
致谢
谢谢各位对apiAutoTest的帮助,谢谢~~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)