原始版本

简书: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

举个例子

假设现在有个实际响应结果字典如下

  1. {"case_002": {
  2. "data": {
  3. "id": 500,
  4. "username": "admin",
  5. "mobile": "12345678",
  6. }},
  7. "case_005": {
  8. "data": {
  9. "id": 511,
  10. "create_time": 1605711095
  11. },
  12. }
  13. }
  • 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中请求参数内容如下:

    1. {
    2. "pagenum": 1,
    3. "pagesize": "12",
    4. "data": &$.case_005.data&,
    5. "userId": &$.case_002.data.id&
    6. }

    代码内部解析后如下:

    1. {
    2. "pagenum": 1,
    3. "pagesize": "12",
    4. "data": {
    5. "id": 511,
    6. "create_time": 1605711095
    7. },
    8. "userId": 500
    9. }

其实不难看出其中规则&jsonpath提取语法&,如果你需要的内容是字符串类型,只需要这样"&jsonpath提取语法&"

上传文件

用例中书写格式,在上传文件栏

  1. # 单文件上传在excel中写法
  2. {"接口中接受文件对象的参数名": "文件路径地址"}
  3. # 多文件上传在excel中写法
  4. {"接口中接受文件对象的参数名": ["文件路径1", "文件路径2"]}

预期结果

用例书写格式

  1. # 断言一个内容
  2. {"jsonpath提取表达式": 预期结果内容}
  3. # 多个断言
  4. {"jsonpath提取表达式1": 预期结果内容1,"jsonpath提取表达式2": 预期结果内容2}

其他优化

  • config.yaml文件中新增可配置初始header,整体代码优化,相比之前,同样测试用例执行下,快了2s左右
  • 将配置文件读取,用例读取整合在read_file.py
  • 移除报告压缩方法
  • 减少日志信息

现依赖处理代码

tools/init.py

  1. #!/usr/bin/env/python3
  2. # -*- coding:utf-8 -*-
  3. """
  4. @project: apiAutoTest
  5. @author: zy7y
  6. @file: __init__.py
  7. @ide: PyCharm
  8. @time: 2020/7/31
  9. """
  10. import json
  11. import re
  12. import allure
  13. from jsonpath import jsonpath
  14. from loguru import logger
  15. def extractor(obj: dict, expr: str = '.') -> object:
  16. """
  17. 根据表达式提取字典中的value,表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data
  18. :param obj :json/dict类型数据
  19. :param expr: 表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data
  20. $.0.1 提取字典中的第一个列表中的第二个的值
  21. """
  22. try:
  23. result = jsonpath(obj, expr)[0]
  24. except Exception as e:
  25. logger.error(f'提取不到内容,丢给你一个错误!{e}')
  26. result = None
  27. return result
  28. def rep_expr(content: str, data: dict, expr: str = '&(.*?)&') -> str:
  29. """从请求参数的字符串中,使用正则的方法找出合适的字符串内容并进行替换
  30. :param content: 原始的字符串内容
  31. :param data: 在该项目中一般为响应字典,从字典取值出来
  32. :param expr: 查找用的正则表达式
  33. return content: 替换表达式后的字符串
  34. """
  35. for ctt in re.findall(expr, content):
  36. content = content.replace(f'&{ctt}&', str(extractor(data, ctt)))
  37. return content
  38. def convert_json(dict_str: str) -> dict:
  39. """
  40. :param dict_str: 长得像字典的字符串
  41. return json格式的内容
  42. """
  43. try:
  44. if 'None' in dict_str:
  45. dict_str = dict_str.replace('None', 'null')
  46. elif 'True' in dict_str:
  47. dict_str = dict_str.replace('True', 'true')
  48. elif 'False' in dict_str:
  49. dict_str = dict_str.replace('False', 'false')
  50. dict_str = json.loads(dict_str)
  51. except Exception as e:
  52. if 'null' in dict_str:
  53. dict_str = dict_str.replace('null', 'None')
  54. elif 'true' in dict_str:
  55. dict_str = dict_str.replace('true', 'True')
  56. elif 'False' in dict_str:
  57. dict_str = dict_str.replace('false', 'False')
  58. dict_str = eval(dict_str)
  59. logger.error(e)
  60. return dict_str
  61. def allure_title(title: str) -> None:
  62. """allure中显示的用例标题"""
  63. allure.dynamic.title(title)
  64. def allure_step(step: str, var: str) -> None:
  65. """
  66. :param step: 步骤及附件名称
  67. :param var: 附件内容
  68. """
  69. with allure.step(step):
  70. allure.attach(json.dumps(var, ensure_ascii=False, indent=4), step, allure.attachment_type.TEXT)

tools/data_process.py

  1. #!/usr/bin/env/python3
  2. # -*- coding:utf-8 -*-
  3. """
  4. @project: apiAutoTest
  5. @author: zy7y
  6. @file: data_process.py
  7. @ide: PyCharm
  8. @time: 2020/11/18
  9. """
  10. from tools import logger, extractor, convert_json, rep_expr, allure_step
  11. from tools.read_file import ReadFile
  12. class DataProcess:
  13. response_dict = {}
  14. header = ReadFile.read_config('$.request_headers')
  15. have_token = header.copy()
  16. @classmethod
  17. def save_response(cls, key: str, value: object) -> None:
  18. """
  19. 保存实际响应
  20. :param key: 保存字典中的key,一般使用用例编号
  21. :param value: 保存字典中的value,使用json响应
  22. """
  23. cls.response_dict[key] = value
  24. logger.info(f'添加key: {key}, 对应value: {value}')
  25. @classmethod
  26. def handle_path(cls, path_str: str) -> str:
  27. """路径参数处理
  28. :param path_str: 带提取表达式的字符串 /&$.case_005.data.id&/state/&$.case_005.data.create_time&
  29. 上述内容表示,从响应字典中提取到case_005字典里data字典里id的值,假设是500,后面&$.case_005.data.create_time& 类似,最终提取结果
  30. return /511/state/1605711095
  31. """
  32. # /&$.case.data.id&/state/&$.case_005.data.create_time&
  33. return rep_expr(path_str, cls.response_dict)
  34. @classmethod
  35. def handle_header(cls, token: str) -> dict:
  36. """处理header
  37. :param token: 写: 写入token到header中, 读: 使用带token的header, 空:使用不带token的header
  38. return
  39. """
  40. if token == '读':
  41. return cls.have_token
  42. else:
  43. return cls.header
  44. @classmethod
  45. def handler_files(cls, file_obj: str) -> object:
  46. """file对象处理方法
  47. :param file_obj: 上传文件使用,格式:接口中文件参数的名称:"文件路径地址"/["文件地址1", "文件地址2"]
  48. 实例- 单个文件: &file&D:
  49. """
  50. if file_obj == '':
  51. return
  52. for k, v in convert_json(file_obj).items():
  53. # 多文件上传
  54. if isinstance(v, list):
  55. files = []
  56. for path in v:
  57. files.append((k, (open(path, 'rb'))))
  58. else:
  59. # 单文件上传
  60. files = {k: open(v, 'rb')}
  61. return files
  62. @classmethod
  63. def handle_data(cls, variable: str) -> dict:
  64. """请求数据处理
  65. :param variable: 请求数据,传入的是可转换字典/json的字符串,其中可以包含变量表达式
  66. return 处理之后的json/dict类型的字典数据
  67. """
  68. if variable == '':
  69. return
  70. data = rep_expr(variable, cls.response_dict)
  71. variable = convert_json(data)
  72. return variable
  73. @classmethod
  74. def assert_result(cls, response: dict, expect_str: str):
  75. """ 预期结果实际结果断言方法
  76. :param response: 实际响应字典
  77. :param expect_str: 预期响应内容,从excel中读取
  78. return None
  79. """
  80. expect_dict = convert_json(expect_str)
  81. index = 0
  82. for k, v in expect_dict.items():
  83. actual = extractor(response, k)
  84. index += 1
  85. logger.info(f'第{index}个断言,实际结果:{actual} | 预期结果:{v} \n断言结果 {actual == v}')
  86. allure_step(f'第{index}个断言', f'实际结果:{actual} = 预期结果:{v}')
  87. assert actual == v

源码地址

master: 分支为最新代码

version1.0: 分支为之前开源的代码(通过字典迭代的方式来处理数据依赖)

Https://gitee.com/zy7y/apiAutoTest.git

Https://github.com/zy7y/apiAutoTest.git

后续打算

目前在公司正在做接口测试,说实话也是摸索着来,以上的优化项都是实际做的过程中突然想到的,然后就更新了

  • 接入用例前后置SQL, 前置SQL目前想的是现在项目中遇到的问题,有些接口没有返回需要的数据,这里就要用前置SQL查询的结果传到请求数据里面了,后置SQL主要是请求后查看数据库中的数据是否变动,形成数据库断言
  • 企业微信推送:目前项目中预想的效果,是后端人员提交代码,自动部署之后,通过gitlab-ci 启动测试代码,进行接口测试完成之后采集allure中的测试结果一有异常/失败用例就发送邮件并进行企业微信推送给领导
  • .... 就不说了还有很多优化项,能力不够好好充电吧,~~

致谢

谢谢各位对apiAutoTest的帮助,谢谢~~~

apiAutoTest-更新2020/11/23的更多相关文章

  1. Goland 2020.2.x 激活码永久破解教程 (最新Goland激活码!2020.11.26亲测可用!)

    在2020.11.26 Goland的用户们又迎来了一次更新,这就导致很多软件打开时候就提示Goland激活码已经失效,码小辫第一时间给各位分享了关于最新Goland激活破解教程! goland已经更 ...

  2. 2020.11最新JAVA环境安装配置

    Windows10下java环境配置 更新:2020年11月25日 电脑环境: windows10 64位 一.下载jdk 首先到Oracle网站下载对应操作系统的jdk安装包. https://ww ...

  3. Beta周第14次Scrum会议(11/23)【王者荣耀交流协会】

    一.小组信息 队名:王者荣耀交流协会 小组成员 队长:高远博 成员:王超,袁玥,任思佳,王磊,王玉玲,冉华 小组照片 二.开会信息 时间:2017/11/23 17:02~17:14,总计12min. ...

  4. ThinkPHP 更新 5.0.23 和 5.1.31

    ThinkPHP 更新 5.0.23 和 5.1.31 FastAdmin 也跟着更新. V1.0.0.20181210_beta 修复 ThinkPHP5.0发布了一个重要安全更新,强烈建议更新 修 ...

  5. 第35次Scrum会议(11/23)【欢迎来怼】

    一.小组信息 队名:欢迎来怼小组成员队长:田继平成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文小组照片 二.开会信息 时间:2017/11/23 17:03~17:24,总计21min.地点:东北师 ...

  6. 2018.11.23 浪在ACM 集训队第六次测试赛

    2018.11.23 浪在ACM 集训队第六次测试赛 整理人:刘文胜 div 2: A: Jam的计数法 参考博客:[1] 万众 B:数列 参考博客: [1] C:摆花 参考博客: [1] D:文化之 ...

  7. Linux编译内核 Ubuntu18.04 -2020.11.04

    Linux编译内核 Ubuntu18.04 -2020.11.04 关闭虚拟机并备份 首先关闭虚拟机,其次直接找到.vmdk所在目录,并压缩该目录实现备份 下载内核源码 Linux内核官网:https ...

  8. 【2020.11.28提高组模拟】T1染色(color)

    [2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...

  9. 青龙+Nvjdc短信登陆对接Xdd-plus推送+Ninja CK登陆教程(11.23更新)

    一.准备工作 1.shh工具(powshell.gitbash等等) 2.购买一台云服务器(阿里云.腾讯云都可以) 3.安装宝塔面板 宝塔Linux面板安装教程 - 2021年8月18日更新 - 7. ...

随机推荐

  1. STM32中断

    STM32的中断分两个类型:内核异常和外部中断. 内核异常不能够被打断,不能被设置优先级(它的优先级是凌驾于外部中断之上的).常见的内核异常有以下几种:复位(reset),不可屏蔽中断(NMI),硬错 ...

  2. 用Hugo在gitee上构建博客(Windows环境下)

    目录 用Hugo在gitee上构建博客(Windows环境下) 1.为什么要用gitee? 2.安装git 3.安装Hugo 4.创建远程仓库 5.搭建博客 (以下所有命令都在git bash中输入) ...

  3. MySQL历史

    MySQL历史 马云生气了 去IOE活动 1979年 研发一个引擎 1996年 发布MySQL1.0 1999年 瑞典注册AB公司 2003年 MySQL 5.0版本 提供试图.存储过程 具有了一些企 ...

  4. CentOS7通过源码安装nginx

    需要先安装安装环境和库: yum install gcc-c++ yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum i ...

  5. 手写Javaweb服务器

    简单web服务器 回忆socket 创建客服端(在httpClient_1包下) public class Client {    public static void main(String[] a ...

  6. Luogu P4957 [COCI2017-2018#6] Alkemija

    题意 有 \(n\) 种已知物质,现在手上有 \(m\) 种,每种无限多个.已知 \(k\) 种反应,每种可以将一些反应物变成一些生成物.求经过这些反应过后最多可以有多少种不同的物质. \(\text ...

  7. StrongArray

    * System类中包含了一个static void arraycopy(object src,int srcops,object dest ,int destpos, int length )方法, ...

  8. Django项目-个人网站之投票模块

    Django项目之个人网站 关注公众号"轻松学编程"了解更多. Github地址:https://github.com/liangdongchang/MyWeb.git 感兴趣的可 ...

  9. CSS中的position属性笔记

    一般有5个属性,分别是:static,absolute,relative,fixed,inherit static 自然定位:这个是默认值,没有定位,再设置top,rignt,bottom,left会 ...

  10. How to refresh datasource args caller[X++]

    To refresh  datasource args caller, you must add override method close on form like source code belo ...