apiAutoTest:支持自定义函数,用例中可调用
0. 前言
apiAutoTest从去年8月以来开源至今,也更新了不少内容,一起来看看吧
第一个版本
- 2020/08/08 增加实际响应存储数据的方法,并在字典可以处理依赖见tools/svae_response.py - 2020/08/09 实现多文件上传,接口中Path参数依赖处理
初步实现用迭代的方式来处理接口中的数据依赖关系
第二个版本
- 2020/11/18 使用re库替换之前的字典迭代方式来处理数据依赖
- 2020/11/21 config.yaml文件中新增基准header设置
- 2020/11/22 支持预期结果中字段断言时使用语法糖来实现动态字段断言
- 2020/12/08 优化断言信息,增加数据库(支持mysql)查询操作, 使用`@pytest.fixture(scope="session")`来托管数据库对象,用例新增sql栏
- 2020/12/16 使用conftest.py 初始化用例, 增加失败重跑机制, 增加运行文件run,优化test_api.py冗余代码
第三个版本
- 2021/01/19 添加数据清洗功能(测试开始前进行数据库备份-分别在服务器和本地进行,测试结束后将备份用以恢复数据-将尝试从服务器和本地恢复到服务器数据库中,docker部署的mysql服务已本地调试通过,直接linux部署的mysql并未测试)
- 2021/02/27 添加hooks.py文件(可在此处自定义方法,并用于用例当中,注意请务必在定义的方法中使用return),移除上次更新的eval语法糖,增加用例处理前的日志
一度说不会再更新维护代码,结果还是慢慢的更新了...
1. 自定义函数实现的故事
这是今天更新的,主要需求来自一个apiAutoTest的学习者反馈,这里感谢他,在此之前另一个小伙伴说他需要用上个接口返回的id字段进行运算, 很多测试框架都有这个功能,但我给apiAutoTest的定位是个工具,也就造个轮子嘛
2. 用例中如何使用自定义函数
2.1 在tools/hooks.py中定义好函数
def get_current_highest():
"""获取当前时间戳"""
return int(time.time())
def sum_data(a, b):
"""计算函数"""
return a + b
2.2 在用例中如何使用该函数
语法糖: @函数名()@: 使用无参数函数
@函数名(参数1, 参数2)@: 向函数传递参数
ps: 函数参数兼容apiAutoTest中的提取依赖语法
&此处为jsonpath语法&
用例(怕截图不清所以就这里简易模拟了两条)
用例编号 | 用例标题 | 接口路径 | 是否执行 | token操作 | 请求方式 | 入参关键字 | 上传文件 | 请求数据 | 后置sql | 预期结果 |
---|---|---|---|---|---|---|---|---|---|---|
case_001 | post请求实现登录 | login | 是 | 写 | post | data | {"username": "admin", "password": "123456"} | select * from user where id=&$.case_002.data.id&; | {"$.meta":{ "msg": "登录成功", "status": 200 }} | |
case_002 | 调试函数sum_data(),从path需要运算 | users/@sum_data(&$.case_001.data.id&, 2)@/ | 是 | 读 | put | data | {"username": "tery","password": @sum_data(&$.case_001.data.id&, 66)@, "timer": @get_current_highest()@, "timer_str": " @get_current_highest()@"} | {"$.meta":{"msg": "设置状态成功", "status": 200}} |
运行日志(运行上述用例得到日志如下)
2021-02-27 16:06:50.538 | DEBUG | api.base_requests:send_request:34 - 用例进行处理前数据:
接口路径: login
请求参数: {"username": "admin", "password": "123456"}
后置sql: select * from user where id=&$.case_002.data.id&;
预期结果: {"$.meta":{ "msg": "登录成功", "status": 200 }}
2021-02-27 16:06:50.765 | INFO | api.base_requests:send_api:81 -
最终请求地址:http://www.ysqorz.top:8888/api/private/v1/login
请求方法:post
请求头:{'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'}
请求参数:{'username': 'admin', 'password': '123456'}
上传文件:None
响应数据:{'data': {'id': 500, 'rid': 0, 'username': 'admin', 'mobile': '12345678', 'email': 'adsfad@qq.com', 'token': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4'}, 'meta': {'msg': '登录成功', 'status': 200}}
2021-02-27 16:06:50.773 | INFO | tools.data_process:save_response:27 - 添加key: case_001, 对应value: {'data': {'id': 500, 'rid': 0, 'username': 'admin', 'mobile': '12345678', 'email': 'adsfad@qq.com', 'token': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4'}, 'meta': {'msg': '登录成功', 'status': 200}}
2021-02-27 16:06:50.775 | INFO | tools.data_process:assert_result:115 - 第1个断言,实际结果:{'msg': '登录成功', 'status': 200} | 预期结果:{'msg': '登录成功', 'status': 200}
断言结果 True
2021-02-27 16:06:50.775 | DEBUG | api.base_requests:send_request:34 - 用例进行处理前数据:
接口路径: users/@sum_data(&$.case_001.data.id&, 2)@/
请求参数: {"username": "tery","password": @sum_data(&$.case_001.data.id&, 66)@, "timer": @get_current_highest()@, "timer_str": " @get_current_highest()@"}
后置sql:
预期结果: {"$.meta":{"msg": "设置状态成功", "status": 200}}
2021-02-27 16:06:50.775 | DEBUG | tools:rep_expr:45 - &$.case_001.data.id& 替换的值为 500
2021-02-27 16:06:50.775 | DEBUG | tools:rep_expr:50 - 执行hooks函数sum_data(500, 2) 替换的值为 502
2021-02-27 16:06:50.775 | DEBUG | tools:rep_expr:45 - &$.case_001.data.id& 替换的值为 500
2021-02-27 16:06:50.783 | DEBUG | tools:rep_expr:50 - 执行hooks函数sum_data(500, 66) 替换的值为 566
2021-02-27 16:06:50.783 | DEBUG | tools:rep_expr:50 - 执行hooks函数get_current_highest() 替换的值为 1614413210
2021-02-27 16:06:50.783 | DEBUG | tools:rep_expr:50 - 执行hooks函数get_current_highest() 替换的值为 1614413210
2021-02-27 16:06:50.835 | INFO | api.base_requests:send_api:81 -
最终请求地址:http://www.ysqorz.top:8888/api/private/v1/users/502/
请求方法:put
请求头:{'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36', 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE2MTQ0MTMyMTAsImV4cCI6MTYxNDQ5OTYxMH0.cZTYLARKNj8SKlPGPdIUh9RmyQaYAJnJrLObaKiNiU4'}
请求参数:{'username': 'tery', 'password': 566, 'timer': 1614413210, 'timer_str': ' 1614413210'}
上传文件:None
响应数据:{'data': {'id': 502, 'username': 'linken', 'role_id': 34}, 'meta': {'msg': '更新成功', 'status': 200}}
2021-02-27 16:06:50.835 | INFO | tools.data_process:save_response:27 - 添加key: case_002, 对应value: {'data': {'id': 502, 'username': 'linken', 'role_id': 34}, 'meta': {'msg': '更新成功', 'status': 200}}
2021-02-27 16:06:50.835 | INFO | tools.data_process:assert_result:115 - 第1个断言,实际结果:{'msg': '更新成功', 'status': 200} | 预期结果:{'msg': '设置状态成功', 'status': 200}
断言结果 False
2021-02-27 16:06:53.418 | SUCCESS | __main__:run:43 - 报告已生成
分析日志
从上述日志可以看出语法糖@sum_data(&$.case_001.data.id&, 2)@
运行之后的结果为502
, 其处理的顺序则是先&$.case_001.data.id&
提取出来得到的值是500, 然后调用函数sum_data(500, 2),然后运行这个函数并把结果502 与@sum_data(&$.case_001.data.id&, 2)@
进行替换.
3. 自定义函数实现代码
因为这里用例读取出来的内容是字符串,我影响中反射应该能做到这一点,然后就去找了下资料,然后我又找到
getattr()
这个内置函数但是这个函数不能解决用例带参数的函数问题,然后我把目光移到了exec()
内置函数,该函数可以执行字符串的Python代码,然而我又遇到了问题,该函数里面执行的Python代码变量在其他函数中不能顺利取出来用,最后我找到了资料 在函数内部使用locals()得到一个局部变量字典,通过字典取值的方式 把exec中的变量 取出来
tools/hooks.py
#!/usr/bin/env python
# _*_ coding: utf-8 _*_
"""
@project: apiAutoTest
@file: hooks.py
@author: zy7y
@time: 2021/2/27
@site: https://cnblogs.com/zy7y
@github: https://github.com/zy7y
@gitee: https://gitee.com/zy7y
@desc: 扩展方法, 2021/02/27
关于exec执行python代码可查阅资料:https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p23_executing_code_with_local_side_effects.html
"""
import time
def exec_func(func: str) -> str:
"""执行函数(exec可以执行Python代码)
:params func 字符的形式调用函数
: return 返回的将是个str类型的结果
"""
# 得到一个局部的变量字典,来修正exec函数中的变量,在其他函数内部使用不到的问题
loc = locals()
exec(f"result = {func}")
return str(loc['result'])
def get_current_highest():
"""获取当前时间戳"""
return int(time.time())
def sum_data(a, b):
"""计算函数"""
return a + b
tools/__init__.py
写到这里发现篇幅过长了,所以这里就把改动的地方代码贴出来吧
#!/usr/bin/env/python3
# -*- coding:utf-8 -*-
"""
@project: apiAutoTest
@author: zy7y
@file: __init__.py.py
@ide: PyCharm
@time: 2020/7/31
"""
import json
import re
import allure
from jsonpath import jsonpath
from loguru import logger
from tools.hooks import *
... 上方代码省略
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)))
logger.debug(f"&{ctt}& 替换的值为 {str(extractor(data, ctt))} ")
# 增加自定义函数得的调用,函数写在tools/hooks.py中
for func in re.findall('@(.*?)@', content):
content = content.replace(f'@{func}@', exec_func(func))
logger.debug(f"执行hooks函数{func} 替换的值为 {exec_func(func)}")
return content
... 下方代码省略
4. 源码地址
github: https://www.github.com/zy7y/apiAutoTest
gitee: https://www.gitee.com/zy7y/apiAutoTest
其中最早版本(采用字典迭代方式处理依赖)在 version1.0分支
5. 致谢
感谢所有给予我帮助的人,文章,正在学习或使用apiAutoTest的同学们,其实个人开源这个项目以来个人有得到成就感,非常感谢
参考资料:
apiAutoTest:支持自定义函数,用例中可调用的更多相关文章
- sql自定义函数及C#中调用
1.在C#中调用sql自定义函数 1.1 标量值函数 sql语句调用 select dbo.GetClassIDWithName(1) string strSql = string.Format(& ...
- mysql自定义函数与过程中写法的注意事项
BEGIN #Routine body goes here... /* update szzx_goods_common set gc_id=i where gc_name=(SELECT gc_na ...
- PHP自定义函数格式化json数据怎么调用?
<?php/*** Formats a JSON string for pretty printing** @param string $json The JSON to make pretty ...
- 给MySQL增加mysql-udf-http和mysql-udf-json自定义函数,让MySQL有调用http接口和查询直接回JSON的能力
1.安装mysql-udf-httpyum install -y libcurl*下载地址:http://pan.baidu.com/s/1nuYZqR3tar zxvf mysql-udf-http ...
- 【转载】 Sqlserver中查看自定义函数被哪些对象引用
Sqlserver数据库中支持自定义函数,包含表值函数和标量值函数,表值函数一般返回多个数据行即数据集,而标量值函数一般返回一个值,在数据库的存储过程中可调用自定义函数,也可在该自定义函数中调用另一个 ...
- Entity Framework 6 Recipes 2nd Edition(10-5)译 -> 在存储模型中使用自定义函数
10-5. 在存储模型中使用自定义函数 问题 想在模型中使用自定义函数,而不是存储过程. 解决方案 假设我们数据库里有成员(members)和他们已经发送的信息(messages) 关系数据表,如Fi ...
- [原创]java WEB学习笔记42:带标签体的自定义标签,带父标签的自定义标签,el中自定义函数,自定义标签的小结
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- Mysql5.7创建存储过程中调用自定义函数报错Not allowed to return a result set from a function
因为很多存储过程都会共用一段sql语句,所以我把共用的sql封装成一个自定义函数 AddCapital(); 然后通过存储过程调用,创建存储过程会报错1415,Not allowed to retur ...
- MySQL自定义函数与存储过程
1.前置条件 MySQL数据库中存在表user_info,其结构和数据如下: mysql> desc user_info; +-----------+----------+------+---- ...
随机推荐
- 2019牛客暑期多校训练营(第五场)E.independent set 1(状压dp)
题意:给你n个点 m条边 问你所有子图的最大独立集的和 思路:我们可以设f state 为当前点集下的最大独立集的大小 所以我们可以把集合分为两个部分 绝对包含了这个一个点 绝对不包含这个点 两种情况 ...
- hdu5886Tower Defence(树形dp)
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission ...
- Codeforces Round #575 (Div. 3) B. Odd Sum Segments 、C Robot Breakout
传送门 B题题意: 给你n个数,让你把这n个数分成k个段(不能随意调动元素位置).你需要保证这k个段里面所有元素加起来的和是一个奇数.问可不可以这样划分成功.如果可以打印YES,之后打印出来是从哪里开 ...
- POJ 3189
题意: 给你B个谷仓和n头牛,每个谷仓最多容纳m头牛.此时每头牛对每一个谷仓都有一个喜悦值,你需要把每一头牛都安排某个谷仓内,并且找出来那个每一头牛对它所住的谷仓打的分值,我们对这所有的分值取一个区间 ...
- [CodeForces-629A 用阶乘会爆掉
题意: 给你一个n*n的蛋糕,如果某个位置是'C'那就代表这是一个巧克力块,否则就不是.如果某两个巧克力块在同一行或同一列,那么这个家庭的幸福值就会加1,问你这个家庭的幸福值最大是多少 Input 3 ...
- 牛客编程巅峰赛S1第11场 - 黄金&钻石 A.牛牛的01游戏 (模拟栈)
题意:有一个\(01\)串,两个相邻的\(0\)可以变成一个\(1\),两个相邻的\(1\)可以直接消除,问操作后的字符串. 题解:数组模拟栈直接撸,上代码吧. 代码: class Solution ...
- rabbitMq学习一
1.安装rabbitmq Windows下安装 首先,我们访问官网[https://www.rabbitmq.com/],点击Get Started. 选择下载安装,由于RabbitMQ使用Erlan ...
- [Angular] 删除旧版本,升级安装最新版本
目录 删除旧版本 清除未卸载干净的angular-cli缓存 对于Linux 对于Windows 安装最新版本 查看安装版本 创建新项目 删除旧版本 npm uninstall -g angular- ...
- OpenStack Train版-10.安装neutron网络服务(网络节点:可选)
可选:安装neutron网络服务节点(neutron01网络节点192.168.0.30)网络配置按照官网文档的租户自助网络 配置系统参数 echo 'net.ipv4.ip_forward = 1' ...
- unbuntu 安装 bochs
参考 https://www.cnblogs.com/HonkerYblogs/p/10285619.html https://blog.csdn.net/time4/article/details/ ...