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的同学们,其实个人开源这个项目以来个人有得到成就感,非常感谢

参考资料:

python3-cookbook

最新文章

  1. UWP学习记录6-设计和UI之控件和模式3
  2. c++中的引用与指针的区别
  3. 简单 常用的git命令
  4. CSS3初学篇章_2(伪类选择符)
  5. Nginx reopen reload作用及工作过程
  6. RMAN_学习笔记1_RMAN Structure概述和体系结构
  7. 利用dropbox备份vps数据
  8. Quartz 2D画虚线-b
  9. ASP.NET中的Request、Response、Server对象
  10. 读书笔记之 - javascript 设计模式 - 观察者模式
  11. python【第三篇】函数
  12. C++默认构造函数的一点说明
  13. Django中Q查询及Q()对象
  14. ActiveMQ简单介绍及安装
  15. Mac appStore下载软件报500错误的解决方法
  16. 通过KSoap三方插件解析WebService接口方法
  17. MemSQL Start[c]UP 2.0 - Round 1E. Three strings
  18. Restful设计思想
  19. 编辑输出“Hello World”
  20. npmrc npm配置文件

热门文章

  1. ODS(Operational Data Store)定义
  2. 设计模式(十四)——模板模式(SpringIOC源码分析)
  3. Codeforce 380A Sereja and Prefixes【二分】
  4. Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle(数学/双指针)
  5. codeforces 292E. Copying Data
  6. A. Crazy Town
  7. Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version)(Manacher算法+输出回文字符串)
  8. 病毒侵袭 HDU - 2896 板子题
  9. 实战交付一套dubbo微服务到k8s集群(4)之dubbo微服务底包镜像制作
  10. 2016 最新的 树莓派3 Raspberry Pi 3 上手评测 图解教程 新手必看!(VNC 安装,启动,关闭)