'2.0版本更新'

This commit is contained in:
yushaoqi 2022-04-07 22:11:01 +08:00
parent 0463430a16
commit 5bd86e1f13
179 changed files with 3976 additions and 1048 deletions

1
Cache/work_login_init Normal file
View File

@ -0,0 +1 @@
75326e83b55ea4a745898a117ce7ef34b5e1

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @Time : 2021/11/18 23:05 # @Time : 2022/3/29 17:31
# @Author : 余少琪 # @Author : 余少琪

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/30 13:32
# @Author : 余少琪
from enum import Enum
class AllureAttachmentType(Enum):
"""
allure 报告的文件类型枚举
"""
TEXT = "txt"
CSV = "csv"
TSV = "tsv"
URI_LIST = "uri"
HTML = "html"
XML = "xml"
JSON = "json"
YAML = "yaml"
PCAP = "pcap"
PNG = "png"
JPG = "jpg"
SVG = "svg"
GIF = "gif"
BMP = "bmp"
TIFF = "tiff"
MP4 = "mp4"
OGG = "ogg"
WEBM = "webm"
PDF = "pdf"
@staticmethod
def attachment_types():
return list(map(lambda c: c.value, AllureAttachmentType))

13
Enums/assertType_enum.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/29 18:03
# @Author : 余少琪
from enum import Enum
class AssertType(Enum):
EQUAL = "=="
NOTEQUAL = "!="
IN = "IN"
NO_TIN = "NOTIN"

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/29 17:32
# @Author : 余少琪
from enum import Enum, unique
@unique
class DependentType(Enum):
"""
数据依赖相关枚举
"""
# 依赖响应中数据
RESPONSE = 'response'
# 依赖请求中的数据
REQUEST = 'request'
# 依赖sql中的数据
SQL_DATA = 'sqlData'

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/30 23:06
# @Author : 余少琪
from enum import Enum
class NotificationType(Enum):
""" 自动化通知方式 """
# 默认通知: 不发送
DEFAULT = 0
# 钉钉通知
DING_TALK = 1
# 微信通知
WECHAT = 2
# 邮箱通知
EMAIL = 3
# 飞书通知
FEI_SHU = 4

20
Enums/requestType_enum.py Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/29 17:42
# @Author : 余少琪
from enum import Enum
class RequestType(Enum):
"""
request请求发送请求参数的数据类型
"""
# json 类型
JSON = "JSON"
# PARAMS 类型
PARAMS = "PARAMS"
# data 类型
DATE = "DATE"
# 文件类型
FILE = 'FILE'

46
Enums/yamlData_enum.py Normal file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/29 17:51
# @Author : 余少琪
from enum import Enum
class YAMLDate(Enum):
"""
测试用例相关字段
"""
# host 配置
HOST = 'host'
# 接口请求的url
URL = 'url'
# 请求方式
METHOD = 'method'
# 请求头
HEADER = 'headers'
# 请求类型
REQUEST_TYPE = 'requestType'
# 是否执行
IS_RUN = 'is_run'
# 请求参数
DATA = 'data'
# 是否依赖用例
DEPENDENCE_CASE = 'dependence_case'
# 依赖用例参数
DEPENDENCE_CASE_DATA = 'dependence_case_data'
# 断言内容
ASSERT = 'assert'
# sql内容
SQL = 'sql'
# 用例ID
CASE_ID = 'case_id'
# jsonpath提取
JSONPATH = 'jsonpath'
# 替换的内容
REPLACE_KEY = 'replace_key'
# 依赖数据类型
DEPENDENT_TYPE = 'dependent_type'
# 用例描述
DETAIL = 'detail'

BIN
Files/companySewage.xlsx Normal file

Binary file not shown.

BIN
Files/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

BIN
Files/排入水体名.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

604
README.md
View File

@ -1,6 +1,6 @@
## 框架介绍 ## 框架介绍
本框架主要是基于 Python + pytest + allure + log + yaml + mysql + 钉钉通知 + Jenkins 实现的接口自动化框架。 本框架主要是基于 Python + pytest + allure + log + yaml + mysql + redis + 钉钉通知 + Jenkins 实现的接口自动化框架。
* git地址: [https://gitee.com/yu_xiao_qi/pytest-auto-api](https://gitee.com/yu_xiao_qi/pytest-auto-api) * git地址: [https://gitee.com/yu_xiao_qi/pytest-auto-api](https://gitee.com/yu_xiao_qi/pytest-auto-api)
* 项目参与者: 余少琪 * 项目参与者: 余少琪
@ -9,11 +9,18 @@
如果对您有帮助,请点亮 小星星 以表支持,谢谢 如果对您有帮助,请点亮 小星星 以表支持,谢谢
![img.png](image/starts.png)
## 框架优势 ## 前言
公司突然要求你做自动化,但是没有代码基础不知道怎么做?或者有自动化基础,但是不知道如何系统性的做自动化,
放在yaml文件中维护不知道如何处理多业务依赖的逻辑
本框架不收取任何费用, 其优势在于测试人员直接编写测试用例,运行框架可自动生成测试代码。 那么这里 Gitte 中开源的自动化框架,将为你解决这些问题。
框架支持多环境、多角色任意切换,支持接口响应断言以及数据库断言。 框架主要使用 python 语言编写,结合 pytest 进行二次开发,用户仅需要在 yaml 文件中编写测试用例,
编写成功之后,会自动生成 pytest 的代码,零基础代码小白,也可以操作。
本框架支持多业务接口依赖多进程执行mysql 数据库断言和 接口响应断言并且用例直接在yaml文件中维护无需编写业务代码
接口pytest框架生成allure报告并且发送 企业微信通知/ 钉钉通知/ 邮箱通知/ 飞书通知,灵活配置。
## 实现功能 ## 实现功能
@ -29,35 +36,57 @@
* 多线程执行 * 多线程执行
## 目录结构 ## 目录结构
├── Cache // 存放缓存文件 ├── Cache // 存放缓存文件
├── config // 配置 ├── config // 配置
│ ├── conf.yaml // 公共配置 │ ├── conf.yaml // 公共配置
│ ├── setting.py // 环境路径存放区域 │ ├── setting.py // 环境路径存放区域
├── data // 测试用例数据 ├── data // 测试用例数据
├── docs // 文档 ├── Enums // 枚举层,用于存放项目中所需的枚举
├── lib // 对象层,用作于接口的调用 ├── File // 上传文件接口所需的文件存放区域
├── log // 日志层 ├── log // 日志层
├── report // 测试报告层 ├── report // 测试报告层
├── test_case // 测试用例代码 ├── test_case // 测试用例代码
├── tool // 所有公共模块的封装 ├── utils // 工具类
│ └── allureDataControl.py // allure报告数据清洗 │ └── assertUtils // 断言
│ └── assertControl.py // 断言模块 │ └── assertUtils .py
│ └── cacheControl.py // 缓存模块 │ └── cacheUtils // 缓存处理模块
│ └── dingtalkControl.py // 钉钉发送通知 │ └── cacheControl.py
│ └── excelControl.py // 读取excel文件 │ └── redisControl.py
│ └── gettimeControl.py // 时间模块 │ └── logUtils // 日志处理模块
│ └── logControl.py // 日志模块 │ └── logControl.py
│ └── logDecorator.py // 日志装饰器 │ └── logDecoratrol.py // 日志装饰器
│ └── mysqlControl.py // 数据库模块 │ └── runTimeDecoratrol.py // 统计用例执行时长装饰器
│ └── regularControl.py // 正则模块 │ └── mysqlUtils // 数据库模块
│ └── requestControl.py // 请求模块 │ └── get_sql_data.py
│ └── runtimeControl.py // 响应时长统计模块 │ └── mysqlControl.py
│ └── sendmailControl.py // 发送邮件 │ └── noticUtils // 通知模块
│ └── testcaseAutomaticControl.py // 自动生成测试代码 │ └── dingtalkControl.py // 钉钉通知
│ └── yamlControl.py // yaml文件 │ └── feishuControl.py // 飞书通知
├── Readme.md // help │ └── sendmailControl.py // 邮箱通知
│ └── weChatSendControl.py // 企业微信通知
│ └── otherUtils // 其他工具类
│ └── allureDate // allure封装
│ └── allure_report_data.py // allure报告数据清洗
│ └── allure_tools..py // allure 方法封装
│ └── localIpControl.py // 获取本地IP
│ └── threadControl.py // 定时器类
│ └── readFilesUtils // 文件操作
│ └── caseAutomaticControl.py // 自动生成测试代码
│ └── clean_files.py // 清理文件
│ └── excelControl.py // 读写excel
│ └── get_all_files_path.py // 获取所有文件路径
│ └── get_yaml_data_analysis.py // yaml用例数据清洗
│ └── regularControl.py // 正则
│ └── yamlControl.py // yaml文件读写
│ └── recordingUtils // 代理录制
│ └── mitmproxyContorl..py
│ └── requestsUtils
│ └── dependentCase.py // 数据依赖处理
│ └── requestControl..py // 请求封装
│ └── timeUtils
├── Readme.md // help
├── pytest.ini ├── pytest.ini
├── run.py // 运行入口 ├── run.py // 运行入口
## 依赖库 ## 依赖库
@ -99,140 +128,433 @@
xlutils==2.0.0 xlutils==2.0.0
xlwt==1.3.0 xlwt==1.3.0
#### 安装Python、Pip环境创建虚拟环境
```
一、安装Python环境
# 1、下载Python程序
# Python包地址https://www.python.org/ftp/python/
wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz
# 2、解压Python-3.8.5.tgz
tar -zxvf Python-3.8.5.tgz
# 3、编译安装
sudo mkdir /usr/local/python3.8.5
cd Python-3.8.5
sudo ./configure --prefix=/usr/local/python3.8.5
sudo make && sudo make install
# 4、建立软链接
sudo ln -s /usr/local/python3.8.5/bin/python3 /usr/bin/python3
sudo ln -s /usr/local/python3.8.5/bin/pip3 /usr/bin/pip3
# 5、验证安装
python3 -V
pip3 -V
二、安装虚拟环境、创建虚拟环境
# 1、安装虚拟环境virtualenv
yum install -y python-virtualenv
# 2、创建虚拟环境
# 在项目根目录创建虚拟环境
virtualenv -p python3 venv
# 3、激活虚拟环境
source ./venv/bin/activate
# 4、退出虚拟环境
deactivate
```
## 安装教程 ## 安装教程
输入如下命令,安装本框架的所有第三方库依赖 输入如下命令,安装本框架的所有第三方库依赖
pip install -r requirements.txt pip install -r requirements.txt
## 使用说明 ## 用例中相关字段的介绍
### config-->conf.yaml ![img.png](image/case_datas.png)
![img.png](images/config/conf.png) 上方截图,就是一个用例中需要维护的相关字段,下面我会对每个字段的作用,做出解释。
首先是配置文件,这里主要存放了一个公共的配置数据,如项目名称、钉钉、邮箱、企业微信、数据库等相关的配置全部都在这里 1、case_common: 这个公共参数的维护,方便后期如有需要新增的字段,可以添加在公共参数中,
所有的字段在conf.yaml中都有相关的注释自行修改即可。 目前只有三个allureEpic、allureFeature、allureStory
这三个都是allure报告需要用到的装饰器内容后续自动生成 pytest 中 test_case 会用到这三个参数值。
2、spu_apply_list_01 用例ID唯一
3、host: 接口的域名: 填写规则如下 ${{host}}执行脚本时会去读取conf.yaml 文件中配置域名
4、url: 接口路径
5、header: 请求头
6、requestType: 请求参数类型有json、file、params、data四种类型
7、is_run: 是否执行,为空、或者 True 都会执行
8、data: 请求参数,所有的请求参数,全部放在 data 下方
9、dependence_case判断该条用力是否有依赖业务如为空或者 false 则表示没有
10、dependence_case_data依赖用例中需要的相关数据下方数据依赖示例中会对该字段下方的内容做详细介绍
11、assert: 断言支持判断sql、或者接口响应内容。
12、sql该用例中所需使用的sql
目前框架主要是用的企业微信通知,在用例执行成功之后发送通知,通知内容如下,可以根据公司主要使用的通讯工具自行更改。 ### 如何发送get请求
在公共方法中分别封装了钉钉通知、以及邮箱通知。 上方了解了用例的数据结构之后下面我们开始编写第一个get请求方式的接口。
首先,开始编写项目之后,我们在 conf.yaml 中配置项目的域名
# 注意点: ![img.png](image/conf.png)
# 之前为了小伙伴们拉下代码执行的时候不受影响,企业微信、钉钉、邮箱的通知配置的都是我的
# 我发现很多拉代码的小伙伴这里配置都没改,所有的通知都发到我这里来了哦~~麻烦看到这里的小伙伴自己在conf.yaml改一下相关配置
![img.png](images/config/wechat.png) 域名配置好之后,我们来编写测试用例,在 data 文件下面,创建一个名称为
spu_apply_list.yaml 的用例文件,内容如下
如程序执行执行异常时,会自动收集错误信息,并将内容发送邮件。 # 公共参数
case_common:
![img.png](images/data/email.png) allureEpic: 电商平台端
allureFeature: 审核中心
### config --> setting.py allureStory: 商品审核列表
setting.py 文件主要是用来存放项目中所有文件的目录地址
更改过一些公用的配置之后,下面我们来开始编写自动化
### data 用来存放测试用例
![img.png](images/data/data.png)
上方主要是测试用例,测试用例是整个自动化程序中非常重要的一部分,需要严格按照我上方图中的格式进行编写。
下面我会对每个字段依次进行解释对应的作用。
- url: 请求接口的地址,${{MerchantHost}} 为接口的host放在conf.yaml 文件中可以更改成公司项目的host
- method: 请求方式目前支持GET、POST、DELETE、PUT本人公司目前设计到的请求方式只有这四种如有需求可自行添加
- detail: 用例描述,程序中未强制要求必填,但是最好是每个用例都填写上,打印日志以及生成代码的函数注释,都会依赖用例描述
- header: 请求头
- requestType: 必填这个字段主要取决于你请求的是参数是以json、params、file、或者data的格式
- data: 请求参数
如接口中需要的请求参数全部放在data中
- allureEpic: 作用与allure装饰器必填如有多个测试用例只需要写在第一个用例中就行
- allureFeature: 作用于allure装饰器必填如有多个测试用例只需要写在第一个用例中就行
- resp: 响应断言相关的数据
- 响应接口的参数字段(如code): code就是接口的响应状态码这些参数都是自己加的。
- jsonpath: 这里获取到对应的接口数据主要使用到了jsonpath。如果有不了这一块的大家可以看我的博客https://blog.csdn.net/weixin_43865008/article/details/118371620
- value: 预期值这里会根据你前面jsonpath中获取到的响应数据然后和你添加的预期值进行断言。如果断言失败会打印对应的日志信息以及allure测试报告中也会呈现这条用例的失败状态
- type: 断言的类型,如判断是否相等,则使用”==“,或者”!=“则表示内容不相等”IN“则表示预期值是否在响应值中对应的还有"NOTIN"
- AssertType: 目前自动化支持两种断言类型接口响应断言和数据库断言。如果是接口响应断言则AssertType的值可不填如果值为"SQL"的话则走数据库断言。为sql的时候sql查询出来的数据类型是字典类型因此value值会从sql查询出来的字段中使用jsonpath的形式读取sql查询出来的数据
- 如有多个数据,则可像上方图中一样,创建多个字段
- sql: sql 是以 LIST 的类型存储的可以将我们这个接口需要依赖的sql语句全部放在这里程序中会循环查询出sql中的所有语句并且返回数据库中的值从而与接口响应的值做匹配。这里也是对于sql多表联查不太会的朋友的福音。如果不会多表联查的话可以编写单表sql程序中会将所有单表的数据内容全部查询出来
- 接口中如有多条测试用例,则以上方格式为例,添加多个即可。
## lib---> xxx.py
假设我们按照上方图中的格式内容创建了一个用例创建成功之后yaml文件的用例创建之后下面我们来生成自动化脚本执行第一条用例。
![img.png](images/lib/writecase.png)
首先我们找到tools目录下的 testcaseAutomaticControl.py 文件,然后执行这里的代码
执行成功之后我们可以看到lib和test_case目录下会生成一个和创建用例yaml文件名称一模一样的py文件,test_case名称会以test_开头
内容如下:
![img.png](images/lib/lib.png)
下面我们就可以开始执行我们的测试用例了这里生成的文件主要类似于我们自动化模型中的PO模型生成的page
spu_apply_list_01:
host: ${{host}}
url: /api/v1/work/spu/approval/spuList
method: GET
detail: 查看商品审核列表
headers:
Content-Type: application/json;charset=UTF-8
token: work_login_init
# 请求的数据,是 params 还是 json、或者file
requestType: params
# 是否执行,空或者 true 都会执行
is_run: False
data:
spuType: 1
pageNum: 1
pageSize: 10
# 是否有依赖业务为空或者false则表示没有
dependence_case:
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:
执行之后我们可以看到下方详细的请求日志信息方便我们进行用例调试lie层只是作用域单个接口的调试如果需要多接口跑业务的话直接到test_case层去做即可 get请求我们 requestType 写的是params这样发送请求时我们会将请求参数拼接中url中最终像服务端发送请求的地址格式会为
![img.png](images/lib/runcase.png) ${{host}}/api/v1/work/spu/approval/spuList?supType=1&pageNum=1&pageSize=10
test_case --> test_apply_verifycode.py ### 如何发送post请求
__用例调试成功之后下面我们进入编写用例脚本阶段主要内容如下__ # 公共参数
case_common:
allureEpic: 盲盒APP
allureFeature: 登录模块
allureStory: 获取登录验证码
send_sms_code_01:
host: ${{host}}
url: /mobile/sendSmsCode
method: POST
detail: 正常获取登录验证码
headers:
appId: '23132'
masterAppId: masterAppId
Content-Type: application/json;charset=UTF-8
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run:
data:
phoneNumber: "180xxxx9278"
# 是否有依赖业务为空或者false则表示没有
dependence_case: False
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: '00000'
AssertType:
success:
jsonpath: $.success
type: ==
value: true
AssertType:
sql:
这里post请求我们需要请求的数据格式是json格式的那么requestType 则填写为json格式。包括 PUT/DELETE/HEAD 请求的数据格式都是一样的,唯一不同的就是需要配置 reuqestType如果需要请求的参数是json格式则requestType我们就填写json如果是url拼接的形式我们就填写 params
![img.png](images/testcases/img.png) ### 如何测试上传文件接口
其中代码中关于pytest的相关内容网上的资料有非常多并且非常详情这里不做赘述。 首先,我们将所有需要测试的文件,全部都放在 files 文件夹中
![img.png](image/files.png)
所有的用例内容格式都为统一的目前只能生成单接口的业务用例如果需要接口执行的话还需要在case层调用对应业务的接口 requestType: file
# 是否执行,空或者 true 都会执行
is_run:
data:
file:
# file 直接写文件名称
files:排入水体名.png
用例添加完成之后执行run.py程序会执行所有文件的用例并且生成测试报告发送钉钉通知。 # 是否有依赖业务为空或者false则表示没有
dependence_case: False
在yaml文件中我们需要注意两个地方主要是用例中的requestType、和 filename 字段:
1、requestType: 上传文件,我们需要更改成 file
2、filename 参数名称: 上传文件我们只需要填写files文件夹下的文件名称即可程序在发送请求时会去识别文件
### 多业务逻辑,如何编写测试用例
多业务这一块,我们拿个简单的例子举例,比如登录场景,在登陆之前,我们需要先获取到验证码。
![img.png](image/send_sms_code.png)
![img.png](image/login.png)
首先,我们先创建一个 get_send_sms_code.yaml 的文件,编写一条发送验证码的用例
# 公共参数
case_common:
allureEpic: 盲盒APP
allureFeature: 登录模块
allureStory: 获取登录验证码
send_sms_code_01:
host: ${{host}}
url: /mobile/sendSmsCode
method: POST
detail: 正常获取登录验证码
headers:
appId: '23132'
masterAppId: masterAppId
Content-Type: application/json;charset=UTF-8
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run:
data:
phoneNumber: "180****9278"
# 是否有依赖业务为空或者false则表示没有
dependence_case: False
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: '00000'
AssertType:
success:
jsonpath: $.success
type: ==
value: true
AssertType:
sql:
编写好之后,我们在创建一个 login.yaml 文件
# 公共参数
case_common:
allureEpic: 盲盒APP
allureFeature: 登录模块
allureStory: 登录
login_02:
host: ${{host}}
url: /login/phone
method: POST
detail: 登录输入错误的验证码
headers:
appId: '23132'
masterAppId: masterAppId
Content-Type: application/json;charset=UTF-8
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run:
data:
phoneNumber: 18014909278
code:
# 是否有依赖业务为空或者false则表示没有
dependence_case: True
# 依赖的数据
dependence_case_data:
- case_id: send_sms_code_02
dependent_data:
- dependent_type: response
jsonpath: $.code
replace_key: $.data.code
assert:
code:
jsonpath: $.code
type: ==
value: '00000'
AssertType:
sql:
其中处理多业务的核心区域,主要在这里:
dependence_case: True
# 依赖的数据
dependence_case_data:
- case_id: send_sms_code_02
dependent_data:
- dependent_type: response
jsonpath: $.code
replace_key: $.data.code
首先,我们 dependence_case 需要设置成 True并且在下面的 dependence_case_data 中设计相关依赖的数据。
1、case_id上方场景中我们登录需要先获取验证码因此依赖的case_id 就是发送短信验证码的 case_id send_sms_code_02
2、dependent_type我们依赖的是获取短信验证码接口中的响应内容因此这次填写的是 response
3、jsonpath: 通过jsonpath 提取方式,提取到短信验证码中的验证码内容
4、replace_key拿到验证码之后我们将本条用例中的data中的code参数那么我们使用jsonpath的方式进行替换 $.data.code
### 多业务逻辑,需要依赖同一个接口中的多个数据
dependence_case_data:
- case_id: send_sms_code_02
dependent_data:
# 提取接口响应的code码
- dependent_type: response
jsonpath: $.code
replace_key: $.data.code
# 提取接口响应的accessToken
- dependent_type: response
jsonpath: $.data.accessToken
# 替换请求头中的accessToken
replace_key: $.headers.accessToken
### 关于框架未来功能的规划 如上方示例,可以添加多个 dependent_type
1、计划后期多接口业务逻辑也统一放在yaml文件中维护并且生成对应业务逻辑的相关代码 ### 多业务逻辑,需要依赖不同接口的数据
2、自动生成代码的功能计划通过多线程实现 假设我们需要获取 send_sms_code_01、get_code_01两个接口中的数据用例格式如下
dependence_case: True
# 依赖的数据
dependence_case_data:
- case_id: send_sms_code_01
dependent_data:
# 提取接口响应的code码
- dependent_type: response
jsonpath: $.code
replace_key: $.data.code
- case_id: get_code_01
dependent_data:
# 提取接口响应的code码
- dependent_type: response
jsonpath: $.code
replace_key: $.data.code
### 用例中需要依赖登录的token如何设计
首先为了防止重复请求调用登录接口pytest中的 conftest.py 提供了热加载机制,看上方截图中的代码,我们需要在 conftest.py 提前编写好登录的代码。
![img.png](image/conftest.png)
如上方代码所示我们会先去读取login.yaml文件中的用例然后执行获取到响应中的token然后 编写 Cache('work_login_init').set_caches(token)将token写入缓存中其中 work_login_init 是缓存名称。
编写好之后,我们会在 requestControl.py 文件中读取缓存中的token如果该条用例需要依赖token则直接进行内容替换。
![img.png](image/token.png)
这里在编写用例的时候token 填写我们所编写的缓存名称即可。
### 用例中如何生成随机数据
比如我们有些特殊的场景,可能会涉及到一些定制化的数据,每次执行数据,需要按照指定规则随机生成。
![img.png](image/randoms.png)
如上图所示,我们用例中的 reason 审核原因后方,需要展示审核的当前时间。那么我们首先需要封装一个获取当前时间的方法
![img.png](image/regular.png)
那么我们就在 regularControl.py 文件中,编写 get_time 的方法。编写好之后,在用例中编写规则如下:
reason: 审核时间${{get_time}}
使用 ${{函数名称}}的方法程序调用时会生成当前时间。在regularControl.py 文件中,我还封装了一些常用的随机数,如随机生成男生姓名、女生姓名、身份证、邮箱、手机号码之类的,方便大家使用。 如,随机生成邮箱,我们在用例中编写的格式为 ${{get_email}} 。
其他所需随机生成的数据,可在文件中自行添加。
### 用例中如何进行接口断言和数据库断言
假设现在我需要测试一个报表统计的数据,该接口返回了任务的处理时长 和 处理数量。功能如下截图所示:
![img.png](image/question_coun.png)
假设下方是我们拿到接口响应的数据内容:
{"code": 200, "times": 155.91, "counts": 9}
这个时候我们需要判断该接口返回的数据是否正确就需要编写sql对响应内容进行校验。
![img.png](image/sql.png)
因此我们编写了如上sql查出对应的数据那么用例中编写规则如下下方我们分别断言了两个内容一个是对接口的响应code码进行断言一个是断言数据库中的数据。
assert:
code:
jsonpath: $.code
type: ==
value: 200
# 断言接口响应时,可以为空
AssertType:
do_time:
# jsonpath 拿到接口响应的数据
jsonpath: $.times
type: ==
# sql 查出来的数据,是字典类型的,因此这里是从字段中提取查看出来的字段
value: $.do_time
# 断言sql的时候AssertType 的值需要填写成 SQL
AssertType: SQL
question_counts:
jsonpath: $.counts
type: ==
#
value: $.question_counts
# 断言sql的时候AssertType 的值需要填写成 SQL
AssertType: SQL
sql:
- select * from test_goods where shop_id = 515
我们分别对用例的数据进行讲解,首先是响应断言, 编写规则如下
code:
# 通过jsonpath获取接口响应中的code {"code": 200, "times": 155.91, "counts": 9}
jsonpath: $.code
type: ==
value: 200
# 断言接口响应时,可以为空
AssertType:
下面是对sql进行断言
question_counts:
# 断言接口响应的问题上报数量counts {"code": 200, "times": 155.91, "counts": 9}
jsonpath: $.counts
type: ==
# 查询sql我们数据库查到的数据是一个字段数据是这样的{question_counts: 13, do_time: 1482.70}, 这里我们通过 jsonpath获取question_counts
value: $.question_counts
# 断言sql的时候AssertType 的值需要填写成 SQL
AssertType: SQL
sql:
- SELECT round( sum(( UNIX_TIMESTAMP( filing_time )- UNIX_TIMESTAMP( report_time )) / 60 ) / 60, 2 ) AS do_time, count( id ) AS question_counts FROM fl_report_info WHERE state IN ( 1, 3 )
有些细心的小伙伴会发现我们的sql是列表类型的。这样就意味这我们的sql可以同时编写多条这样会对不会编写多表联查的小伙伴比较友好可以进行单表查询获取我们需要的数据。
sql:
- select * from users;
- select * from goods;
### 自动生成test_case层代码
小伙伴们在编写好 yaml 用例之后,可以直接执行 caseAutomaticControl.py ,会跟你设计的测试用例,生成对应的代码。
![img.png](image/write_test_case.png)
### 发送钉钉通知通知
![img.png](image/dingding.png)
### 发送企业微信通知
![img.png](image/wechart.png)
### 日志打印装饰器
![img.png](image/log.png)
在requestControl.py中我单独封装了一个日志装饰器需要的小伙伴可以不用改动代码直接使用如果不需要直接注释或者改成False。控制台将不会有日志输出
### 统计用例运行时长
![img.png](image/run_times.png)
同样,这里封装了一个统计用例运行时长的装饰器,使用改装饰器前,需要先进行导包
from utils.logUtils.runTimeDecoratorl import execution_duration
导入之后调用改装饰器装饰器中填写的用例执行时长以毫秒为单位如这里设置的2000ms那么如果该用例执行大于2000ms则会输出一条告警日志。
@execution_duration(2000)
### 生成allure报告
我们直接运行主程序 run.py 运行完成之后就可以生成漂亮的allure报告啦~
![img.png](image/allure.png)
![img.png](image/allure2.png)
### 其他
本框架为2.0升级版本升级之后的功能现在基本上都是在yaml中维护用例无需测试人员编写代码
和 1.0版本的区别在于1.0版本还需要测试人员手动编写多业务逻辑的代码,需要有一定基础编码的能力
但是1.0版本同样也可以自动生成代码yaml中维护数据对相对简单如果偏于yaml简单维护的同学可以切换查看1.0分支
下方是1.0分支的操作文档:[点我查看](https://blog.csdn.net/weixin_43865008/article/details/121903028?spm=1001.2014.3001.5502)
******************************************************* *******************************************************

View File

Binary file not shown.

View File

@ -2,15 +2,17 @@ ProjectName:
- 余少琪的框架 - 余少琪的框架
- -
Env: 测试环境
# 测试人员名称,作用于自动生成代码的作者,以及发送企业微信、钉钉通知的测试负责人 # 测试人员名称,作用于自动生成代码的作者,以及发送企业微信、钉钉通知的测试负责人
TestName: 余少琪 TesterName: 余少琪
# 域名1 # 域名1
Host: https://redisdatarecall.csdn.net host: https://www.baidu.com # 域名随便写的,记得修改
app_host:
# 域名2 # 域名2
work: work:
# 报告通知类型:1钉钉 2企业微信通知 3、邮箱 # 报告通知类型:0: 不发送通知 1钉钉 2企业微信通知 3、邮箱通知 4、飞书通知
NotificationType: 2 NotificationType: 2
# 注意点: # 注意点:
@ -24,12 +26,11 @@ DingTalk:
# 数据库相关配置 # 数据库相关配置
MySqlDB: MySqlDB:
# 数据库开关
switch: False switch: False
host: host:
user: user: dev
password: password:
db: test_obp_data db:
# 企业通知的相关配置 # 企业通知的相关配置
@ -39,6 +40,10 @@ WeChat:
email: email:
send_user: 1603453211@qq.com send_user: 1603453211@qq.com
email_host: smtp.qq.com email_host: smtp.qq.com
stmp_key: rzuabbobadbuhadc stamp_key: rzuabbobadbuhadc
# 收件人 # 收件人
send_list: 1603453211@qq.com send_list: 1603453211@qq.com
# 飞书通知
FeiShuTalk:
webhook:

View File

@ -28,23 +28,16 @@ class ConfigHandler:
# 项目路径 # 项目路径
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 测试数据路径 # 用例路径
date_path = os.path.join(root_path, 'data' + _SLASH + "Merchant" + _SLASH + "UserLogin") case_path = os.path.join(root_path, 'test_case' + _SLASH)
# 测试用例数据路径
merchant_data_path = os.path.join(root_path, 'data' + _SLASH)
data_path = os.path.join(root_path, 'data' + _SLASH) data_path = os.path.join(root_path, 'data' + _SLASH)
cache_path = os.path.join(root_path, 'Cache' + _SLASH) cache_path = os.path.join(root_path, 'Cache' + _SLASH)
if not os.path.exists(cache_path):
os.mkdir(cache_path)
case_path = os.path.join(root_path, 'test_case' + _SLASH) log_path = os.path.join(root_path, 'logs' + _SLASH + 'log.log')
# 测试报告路径
report_path = os.path.join(root_path, 'report')
json_path = os.path.join(root_path, 'data' + _SLASH + 'data.json')
log_path = os.path.join(root_path + _SLASH + 'logs')
info_log_path = os.path.join(root_path, 'logs' + _SLASH + 'info.log') info_log_path = os.path.join(root_path, 'logs' + _SLASH + 'info.log')
@ -52,23 +45,20 @@ class ConfigHandler:
warning_log_path = os.path.join(root_path, 'logs' + _SLASH + 'warning.log') warning_log_path = os.path.join(root_path, 'logs' + _SLASH + 'warning.log')
if not os.path.exists(report_path): config_path = os.path.join(root_path, 'config' + _SLASH + 'config.yaml')
os.mkdir(report_path)
config_path = os.path.join(root_path, 'config' + _SLASH + 'conf.yaml') file_path = os.path.join(root_path, 'Files' + _SLASH)
token_yaml_path = os.path.join(root_path, 'data' + _SLASH + 'token.yaml') # 测试报告路径
report_path = os.path.join(root_path, 'report')
excel_path = os.path.join(root_path, 'data' + _SLASH)
# lib 存放po文件 # lib 存放po文件
lib_path = os.path.join(root_path, "lib" + _SLASH) lib_path = os.path.join(root_path, "lib" + _SLASH)
temp_path = os.path.join(root_path, 'report' + _SLASH + 'tmp') # temp_path = os.path.join(root_path, 'report' + _SLASH + 'tmp')
if not os.path.exists(temp_path): # if not os.path.exists(temp_path):
os.mkdir(temp_path) # os.mkdir(temp_path)
html_path = os.path.join(root_path, 'report' + _SLASH + 'html')
if __name__ == '__main__': if __name__ == '__main__':
print(ConfigHandler.temp_path) print(ConfigHandler.cache_path)

32
data/Login/login.yaml Normal file
View File

@ -0,0 +1,32 @@
# 公共参数
case_common:
allureEpic: 电商平台端
allureFeature: 登录模块
allureStory: 正常登录
login:
host: ${{host}}
url: /api/v1/work/user/loginByPassword
method: POST
detail: 正常登录
headers:
Content-Type: application/json;charset=UTF-8
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run: True
data:
param:
phone: '13300000000'
password: '123456'
# 是否有依赖业务为空或者false则表示没有
dependence_case: False
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/3/30 14:56
# @Author : 余少琪
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from config.setting import ConfigHandler
TestData = CaseData(ConfigHandler.data_path + r'WorkApplyCenter/sup_apply_list.yaml').case_process()
print(TestData)
# print([i for i in TestData])
is_run = [i['is_run'] for i in TestData]
print(is_run)

View File

@ -0,0 +1,31 @@
# 公共参数
case_common:
allureEpic: 电商平台
allureFeature: 文件模块
allureStory: PATCH
batchDisable_01:
host: {{host}}
url: /adpt/advert/adplan/batchDisable
method: PATCH
detail: 测试PATCH接口
headers:
Content-Type: application/json;charset=UTF-8
Authorization: work_login_init
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run: Fasle
data:
# 是否有依赖业务为空或者false则表示没有
dependence_case: False
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:

View File

@ -0,0 +1,41 @@
# 公共参数
case_common:
allureEpic: 电商平台端
allureFeature: 审核中心
allureStory: 商品审核
spu_apply_01:
host: ${{host}}
url: /api/v1/work/spu/approval/pass
method: PUT
detail: 正常审核商品(测试一个接口依赖多个数据)
headers:
Content-Type: application/json;charset=UTF-8
token: work_login_init
# 请求的数据,是 params 还是 json、或者file
requestType: json
# 是否执行,空或者 true 都会执行
is_run: False
data:
applyId:
reason: 审核时间${{get_time}}
# 是否有依赖业务为空或者false则表示没有
dependence_case: True
# 依赖的数据
dependence_case_data:
- case_id: spu_apply_list_01
dependent_data:
- dependent_type: response
jsonpath: $.data.data.[0].applyId
replace_key: $.data.applyId
- dependent_type: request
jsonpath: $.detail
replace_key: $.data.reason
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:
- select * from test_goods where shop_id = 515

View File

@ -0,0 +1,34 @@
# 公共参数
case_common:
allureEpic: 电商平台端
allureFeature: 审核中心
allureStory: 商品审核列表
spu_apply_list_01:
host: ${{host}}
url: /api/v1/work/spu/approval/spuList
method: GET
detail: 查看商品审核列表
headers:
Content-Type: application/json;charset=UTF-8
token: work_login_init
# 请求的数据,是 params 还是 json、或者file
requestType: params
# 是否执行,空或者 true 都会执行
is_run: False
data:
spuType: 1
pageNum: 1
pageSize: 10
# 是否有依赖业务为空或者false则表示没有
dependence_case:
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:

View File

@ -0,0 +1,42 @@
# 公共参数
case_common:
allureEpic: 电商平台端
allureFeature: 审核中心
allureStory: 商品审核详情
spuApplyDetails_01:
host: ${{host}}
url: /api/v1/work/spu/approval/spuApplyDetails/$url_param{good_id}
method: GET
detail: 查看商品审核详情
headers:
Content-Type: application/json;charset=UTF-8
token: work_login_init
# 请求的数据,是 params 还是 json、或者file\date
requestType: json
# 是否执行,空或者 true 都会执行
is_run: False
data:
auth:
# 是否有依赖业务为空或者false则表示没有
dependence_case: True
# 依赖的数据
dependence_case_data:
- case_id: spu_apply_list_01
dependent_data:
- dependent_type: response
jsonpath: $.data.data.[0].applyId
replace_key: $url_param{good_id}
- case_id: login_01
dependent_data:
- dependent_type: response
jsonpath: $.data.data.[0].token
replace_key: $.data.auth
assert:
code:
jsonpath: $.code
type: ==
value: 200
AssertType:
sql:

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @Time : 2021/11/18 23:05 # @Time : 2022/3/28 10:49
# @Author : 余少琪 # @Author : 余少琪

View File

@ -1,27 +0,0 @@
- # 用例001
url: ${{Host}}/recommend/get_head_word
method: POST
detail: 测试接口
headers:
Content-Type: application/json;charset=UTF-8
requestType: params
# 测试平台名称
allureEpic: 这里是测试平台名称
# 测试模块名称
allureFeature: 这里是测试模块名称
data:
# 请求类型params 是以url拼接的形式请求json则传的是json串
bid: blog-121903028
resp:
code:
jsonpath: $.status
type: ==
value: 200
AssertType:
msg:
jsonpath: $.msg
type: ==
value: 查询成功
AssertType:
sql:

View File

@ -0,0 +1,36 @@
# 公共参数
case_common:
allureEpic: 婚奢汇
allureFeature: 文件上传
allureStory: 上传excel文件
upload_files_02:
host: {{host}}
url: /adpt/advert/mate/batchUploadMate
method: POST
detail: 测试上传文件接口
headers:
Content-Type: application/json;charset=UTF-8
Authorization: work_login_ini
# 请求的数据,是 params 还是 json、或者file
requestType: file
# 是否执行,空或者 true 都会执行
is_run:
data:
file:
files: test.png
# 是否有依赖业务为空或者false则表示没有
data:
uids: vc-upload-1649224175138-20
params:
test: 1
dependence_case:
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 1011006
AssertType:
sql:

View File

@ -0,0 +1,32 @@
# 公共参数
case_common:
allureEpic: 婚奢汇
allureFeature: 文件上传
allureStory: 上传excel文件
upload_files_01:
host: http://parkyz.kkx88.cn
url: /api/biz/company/importData
method: POST
detail: 测试上传文件接口
headers:
Content-Type: application/json;charset=UTF-8
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjA1ZTc3ZDRiLWU3NWEtNGMyNi04NjA1LTI4M2Q3ZjdlNWNmZSJ9.2htV4vh5KxR9jEDltfifiwZUPd1OYrwLRXRLM2K1AEgoY-jTCn9z1m5aLPmpIIf2dL5YoSEbyxmGT9Pm1CwzMg
# 请求的数据,是 params 还是 json、或者file
requestType: file
# 是否执行,空或者 true 都会执行
is_run:
data:
file:
file: companySewage.xlsx
# 是否有依赖业务为空或者false则表示没有
dependence_case: False
# 依赖的数据
dependence_case_data:
assert:
code:
jsonpath: $.code
type: ==
value: 401
AssertType:
sql:

BIN
image/allure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
image/allure2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
image/case_datas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
image/conf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
image/conftest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
image/dingding.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
image/files.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
image/log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
image/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
image/question_coun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
image/randoms.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
image/regular.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
image/run_times.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
image/send_sms_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
image/sql.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
image/starts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
image/token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
image/wechart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
image/write_test_case.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

View File

@ -1,28 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-03-16 13:07:13
# @Author : 余少琪
from tools.requestControl import RequestControl
from tools.yamlControl import GetCaseData
from config.setting import ConfigHandler
class DateDemo(object):
@staticmethod
def dateDemo(inData):
"""
测试接口
:param inData:
:return:
"""
resp = RequestControl().http_request(inData['method'], inData)
return resp
if __name__ == '__main__':
path = GetCaseData(ConfigHandler.data_path + r'test_demo\DateDemo.yaml').get_yaml_case_data()[0]
data = DateDemo().dateDemo(path)
print(data)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @Time : 2021/11/18 23:05 # @Time : 2022/3/28 10:49
# @Author : 余少琪 # @Author : 余少琪

View File

@ -1,2 +0,0 @@
%(levelname)-8s2022-02-21 18:37:52,617 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\error.log:conftest.py:94 执行失败用例数:1
%(levelname)-8s2022-02-21 18:38:30,385 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\error.log:conftest.py:94 执行失败用例数:0

View File

@ -0,0 +1,8 @@
%(levelname)-8s2022-04-02 20:11:02,465 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:57 断言失败, 预期值:200, 断言类型==, 实际值1011006
%(levelname)-8s2022-04-02 22:38:57,192 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:111 JsonPath值获取失败$.code
%(levelname)-8s2022-04-02 22:39:56,756 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:111 JsonPath值获取失败$.code
%(levelname)-8s2022-04-02 22:45:55,163 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:115 JsonPath值获取失败$.code
%(levelname)-8s2022-04-02 22:46:21,310 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:115 JsonPath值获取失败$.code
%(levelname)-8s2022-04-02 23:02:12,628 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:61 断言失败, 预期值:200, 断言类型==, 实际值1011006
%(levelname)-8s2022-04-02 23:45:41,734 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:61 断言失败, 预期值:200, 断言类型==, 实际值1011006
%(levelname)-8s2022-04-02 23:59:33,188 C:\work\Study\pytest-auto-api3\logs\error.log:assertControl.py:61 断言失败, 预期值:200, 断言类型==, 实际值1011006

View File

@ -1,45 +0,0 @@
%(levelname)-8s2022-02-21 18:37:50,684 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:run.py:19
_ _ _ _____ _
__ _ _ __ (_) / \ _ _| |_ __|_ _|__ ___| |_
/ _` | '_ \| | / _ \| | | | __/ _ \| |/ _ \/ __| __|
| (_| | |_) | |/ ___ \ |_| | || (_) | | __/\__ \ |_
\__,_| .__/|_/_/ \_\__,_|\__\___/|_|\___||___/\__|
|_|
开始执行余少琪的框架项目...
%(levelname)-8s2022-02-21 18:37:52,389 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:logDecorator.py:25
=================================================================================
测试标题: 测试接口
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: https://redisdatarecall.csdn.net/recommend/get_head_word
请求参数类型: params
请求内容: {'bid': 'blog-121903028'}
接口响应内容: {'status': 200, 'msg': '查询成功', 'content': ['自动化测试', 'yaml', 'pytest', 'gitee', 'jenkins'], 'error': False}
数据库断言数据: {'sql': None}
=================================================================================
%(levelname)-8s2022-02-21 18:37:52,391 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:assertControl.py:61 断言成功, 预期值:200, 断言类型==, 实际值200
%(levelname)-8s2022-02-21 18:37:52,391 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:assertControl.py:61 断言成功, 预期值:查询成功, 断言类型==, 实际值查询成功
%(levelname)-8s2022-02-21 18:37:52,617 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:91 执行用例总数: 1
%(levelname)-8s2022-02-21 18:37:52,617 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:92 执行通过用例数:0
%(levelname)-8s2022-02-21 18:37:52,620 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:96 执行异常用例数:0
%(levelname)-8s2022-02-21 18:37:52,620 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:98 执行跳过用例数:0
%(levelname)-8s2022-02-21 18:37:52,621 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:100 执行成功率: 成功率0.00%
%(levelname)-8s2022-02-21 18:38:30,375 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:logDecorator.py:25
=================================================================================
测试标题: 测试接口
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: https://redisdatarecall.csdn.net/recommend/get_head_word
请求参数类型: params
请求内容: {'bid': 'blog-121903028'}
接口响应内容: {'status': 200, 'msg': '查询成功', 'content': ['自动化测试', 'yaml', 'pytest', 'gitee', 'jenkins'], 'error': False}
数据库断言数据: {'sql': None}
=================================================================================
%(levelname)-8s2022-02-21 18:38:30,377 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:assertControl.py:61 断言成功, 预期值:200, 断言类型==, 实际值200
%(levelname)-8s2022-02-21 18:38:30,378 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:assertControl.py:61 断言成功, 预期值:查询成功, 断言类型==, 实际值查询成功
%(levelname)-8s2022-02-21 18:38:30,384 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:91 执行用例总数: 1
%(levelname)-8s2022-02-21 18:38:30,384 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:92 执行通过用例数:1
%(levelname)-8s2022-02-21 18:38:30,385 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:96 执行异常用例数:0
%(levelname)-8s2022-02-21 18:38:30,386 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:98 执行跳过用例数:0
%(levelname)-8s2022-02-21 18:38:30,386 C:\Users\hzxy\PycharmProjects\py_auto_demo\logs\info.log:conftest.py:100 执行成功率: 成功率100.00%

681
logs/info.log.2022-04-03 Normal file

File diff suppressed because one or more lines are too long

237
logs/warning.log.2022-04-03 Normal file
View File

@ -0,0 +1,237 @@
%(levelname)-8s2022-04-02 22:48:54,293 C:\work\Study\pytest-auto-api3\logs\warning.log:assertControl.py:118 该用例当前不执行,跳过断言判断
%(levelname)-8s2022-04-02 22:56:48,119 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '753263e5f81e3b40436d8b7ceffcdf791c5c'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: {'res': False}
数据库断言数据: {'sql': False}
=================================================================================
%(levelname)-8s2022-04-02 22:57:31,857 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532109233fa72ad486c97e471d6ff2d6204'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: {'res': False}
数据库断言数据: {'sql': False}
=================================================================================
%(levelname)-8s2022-04-02 22:59:47,978 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:00:16,779 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:01:45,748 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:01:45,748 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:01:46,083 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:02:12,204 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:02:12,205 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:02:12,817 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:02:12,840 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:02:12,847 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:45:41,234 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:45:41,235 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:45:41,626 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:45:41,645 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:45:41,970 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:59:32,711 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:59:32,711 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:59:32,711 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:59:32,714 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 正常登录
请求方式: POST
请求头: {'Content-Type': 'application/json;charset=UTF-8'}
请求路径: http://work.test.feng-go.com/api/v1/work/user/loginByPassword
请求内容: {'param': {'phone': '13300000000', 'password': '123456'}}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:59:32,715 C:\work\Study\pytest-auto-api3\logs\warning.log:conftest.py:61 登录用例设置的是不执行无法获取到token信息
%(levelname)-8s2022-04-02 23:59:33,111 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:59:33,116 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-02 23:59:33,410 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 查看商品审核列表
请求方式: GET
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'token': '7532b925b258a4c54a7a97e24f668727d0d5'}
请求路径: http://work.test.feng-go.com/api/v1/work/spu/approval/spuList
请求内容: {'spuType': 1, 'pageNum': 1, 'pageSize': 10}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================
%(levelname)-8s2022-04-03 00:00:28,088 C:\work\Study\pytest-auto-api3\logs\warning.log:logDecoratorl.py:51
=================================================================================
该条用例跳过执行.
测试标题: 测试patch请求方式
请求方式: PATCH
请求头: {'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VySWQiOjEyNjU0NzY4OTA2NzI2NzI4MDgsImFjY291bnQiOiJzdXBlckFkbWluIiwidXVpZCI6IjJiOTNlNTNjLWRkM2QtNGM0MS05YjI2LTA1Y2NhM2JjMjNlOSIsInN1YiI6IjEyNjU0NzY4OTA2NzI2NzI4MDgiLCJpYXQiOjE2NDg2ODg2NTgsImV4cCI6MTY0ODc3NTA1OH0.640mDK2Eyid_KjSi0Ijei6VW6LYQZ7oYfGCgq4ajqehACzqfvaG4XJ5lqizRSVhidl7J1XTJZeBYVCjEe0GMdw'}
请求路径: http://39.105.186.77:8082/adpt/advert/adplan/batchDisable
请求内容: {'ids': ['1509343309343780875']}
依赖测试用例: 暂无依赖用例数据
接口响应内容: False
数据库断言数据: False
=================================================================================

View File

@ -1,4 +1,9 @@
[pytest] [pytest]
addopts = -p no:warnings
testpaths = test_case/
python_files = test_*.py
python_classes = Test*
python_function = test_*
markers = markers =
shop: project_shop smoke: 冒烟测试
shop_list: project_shop_list

View File

@ -1,22 +0,0 @@
<environment>
<parameter>
<key>测试平台</key>
<value>测试项目</value>
</parameter>
<parameter>
<key>测试环境</key>
<value>TEST</value>
</parameter>
<parameter>
<key>测试人员</key>
<value>余少琪</value>
</parameter>
<parameter>
<key>邮箱</key>
<value>1603453211@qq.com</value>
</parameter>
<parameter>
<key>python.Version</key>
<value>3.9.0</value>
</parameter>
</environment>

52
run.py
View File

@ -1,23 +1,23 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @Time : 2021/11/26 23:12 # @Time : 2022/3/29 15:01
# @Author : 余少琪 # @Author : 余少琪
import traceback
import pytest
import os import os
from tools.logControl import INFO import traceback
from tools.yamlControl import GetYamlData import pytest
from config.setting import ConfigHandler from utils import project_name
from tools import getNotificationType from utils.logUtils.logControl import INFO
from tools.weChatSendControl import WeChatSend from utils import get_notification_type
from tools.dingtalkControl import DingTalkSendMsg from utils.noticUtils.weChatSendControl import WeChatSend
from tools.sendmailControl import SendEmail from utils.noticUtils.dingtalkControl import DingTalkSendMsg
from utils.noticUtils.sendmailControl import SendEmail
from Enums.notificationType_enum import NotificationType
from utils.noticUtils.feishuControl import FeiShuTalkChatBot
def run(): def run():
# 从配置文件中获取项目名称 # 从配置文件中获取项目名称
project_name = GetYamlData(ConfigHandler.config_path).get_yaml_data()['ProjectName'][0]
try: try:
INFO.logger.info( INFO.logger.info(
""" """
@ -30,15 +30,33 @@ def run():
开始执行{}项目... 开始执行{}项目...
""".format(project_name) """.format(project_name)
) )
pytest.main(['-s', '-W', 'ignore:Module already imported:pytest.PytestWarning', '--alluredir', './report/tmp'])
pytest.main(['-s', '-W', 'ignore:Module already imported:pytest.PytestWarning',
'--alluredir', './report/tmp'])
"""
--reruns: 失败重跑次数
--count: 重复执行次数
-v: 显示错误位置以及错误的详细信息
-s: 等价于 pytest --capture=no 可以捕获print函数的输出
-q: 简化输出信息
-m: 运行指定标签的测试用例
-x: 一旦错误则停止运行
--maxfail: 设置最大失败次数当超出这个阈值时则不会在执行测试用例
"--reruns=3", "--reruns-delay=2"
"""
os.system(r"allure generate ./report/tmp -o ./report/html --clean") os.system(r"allure generate ./report/tmp -o ./report/html --clean")
# 通过配置文件判断发送报告通知类型1钉钉 2企业微信通知 3、邮箱 # 判断通知类型
if getNotificationType() == 1: if get_notification_type() == NotificationType.DEFAULT.value:
pass
elif get_notification_type() == NotificationType.DING_TALK.value:
DingTalkSendMsg().send_ding_notification() DingTalkSendMsg().send_ding_notification()
elif getNotificationType() == 2: elif get_notification_type() == NotificationType.WECHAT.value:
WeChatSend().send_email_notification() WeChatSend().send_wechat_notification()
elif getNotificationType() == 3: elif get_notification_type() == NotificationType.EMAIL.value:
SendEmail().send_main() SendEmail().send_main()
elif get_notification_type() == NotificationType.FEI_SHU.value:
FeiShuTalkChatBot().post()
else: else:
raise ValueError("通知类型配置错误,暂不支持该类型通知") raise ValueError("通知类型配置错误,暂不支持该类型通知")
os.system(f"allure serve ./report/tmp -p 9999") os.system(f"allure serve ./report/tmp -p 9999")

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'Login/login.yaml').case_process()
@allure.epic("电商平台端")
@allure.feature("登录模块")
class TestLogin:
@allure.story("正常登录")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_login(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_login.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'uplpad_file_test/batchDisable.yaml').case_process()
@allure.epic("换社会")
@allure.feature("登录模块")
class TestBatchdisable:
@allure.story("测试patch请求方式")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_batchDisable(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_batchDisable.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'WorkApplyCenter/spu_apply.yaml').case_process()
@allure.epic("电商平台端")
@allure.feature("审核中心")
class TestSpuApply:
@allure.story("商品审核")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_spu_apply(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_spu_apply.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'WorkApplyCenter/sup_apply_list.yaml').case_process()
@allure.epic("电商平台端")
@allure.feature("审核中心")
class TestSupApplyList:
@allure.story("商品审核列表")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_sup_apply_list(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_sup_apply_list.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'WorkApplyCenter/work_apply_good_detail.yaml').case_process()
@allure.epic("电商平台端")
@allure.feature("审核中心")
class TestWorkApplyGoodDetail:
@allure.story("商品审核详情")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_work_apply_good_detail(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_work_apply_good_detail.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -1,21 +1,26 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# @Time : 2021/11/28 23:05 # @Time : 2022/3/30 14:12
# @Author : 余少琪 # @Author : 余少琪
import pytest
import os import os
import pytest
import time
import allure
from utils.requestsUtils.requestControl import RequestControl
from config.setting import ConfigHandler from config.setting import ConfigHandler
from tools.yamlControl import GetYamlData from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.cacheUtils.cacheControl import Cache
from utils.readFilesUtils.get_all_files_path import get_all_files
_PROJECT_NAME = GetYamlData(ConfigHandler.config_path).get_yaml_data()['ProjectName'][0] from utils.logUtils.logControl import WARNING, INFO, ERROR
_TEST_NAME = GetYamlData(ConfigHandler.config_path).get_yaml_data()['TestName'] from Enums.yamlData_enum import YAMLDate
from utils.otherUtils.allureDate.allure_tools import allure_step, allure_step_no
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def clear_report(): def clear_report():
try: try:
for one in os.listdir(ConfigHandler.report_path + f'/tmp'): for one in os.listdir(ConfigHandler.report_path + '/tmp'):
if 'json' in one: if 'json' in one:
os.remove(ConfigHandler.report_path + f'/tmp/{one}') os.remove(ConfigHandler.report_path + f'/tmp/{one}')
if 'txt' in one: if 'txt' in one:
@ -26,3 +31,101 @@ def clear_report():
yield yield
@pytest.fixture(scope="session", autouse=True)
def write_case_process():
"""
获取所有用例写入用例池中
:return:
"""
case_data = {}
# 循环拿到所有存放用例的文件路径
for i in get_all_files(ConfigHandler.data_path):
# 循环读取文件中的数据
case_process = CaseData(i).case_process(case_id_switch=True)
# 转换数据类型
for case in case_process:
for k, v in case.items():
# 判断 case_id 是否已存在
case_id_exit = k in case_data.keys()
# 如果case_id 不存在,则将用例写入缓存池中
if case_id_exit is False:
case_data[k] = v
# 当 case_id 为 True 存在时,则跑出异常
elif case_id_exit is True:
raise ValueError(f"case_id: {k} 存在重复项, 请修改case_id\n"
f"文件路径: {i}")
Cache('case_process').set_caches(case_data)
@pytest.fixture(scope="session", autouse=True)
def work_login_init():
"""
获取平台端的token信息
:return:
"""
login_yaml = CaseData(ConfigHandler.data_path + 'Login/login.yaml').case_process()[0]
res = RequestControl().http_request(login_yaml)
# 将token写入缓存中
if res[0] is not False:
token = res[0]['data']['token']
Cache('work_login_init').set_caches(token)
return token
else:
WARNING.logger.warning("登录用例设置的是不执行无法获取到token信息")
def pytest_collection_modifyitems(items):
"""
测试用例收集完成时将收集到的 item name node_id 的中文显示在控制台上
:return:
"""
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
# 定义单个标签
def pytest_configure(config):
config.addinivalue_line(
"markers", "smoke"
)
@pytest.fixture(scope="function", autouse=True)
def case_skip(in_data):
"""处理跳过用例"""
if in_data['is_run'] is False:
allure.dynamic.title(in_data[YAMLDate.DETAIL.value])
allure_step_no(f"请求URL: {in_data[YAMLDate.IS_RUN.value]}")
allure_step_no(f"请求方式: {in_data[YAMLDate.METHOD.value]}")
allure_step("请求头: ", in_data[YAMLDate.HEADER.value])
allure_step("请求数据: ", in_data[YAMLDate.DATA.value])
allure_step("依赖数据: ", in_data[YAMLDate.DEPENDENCE_CASE_DATA.value])
allure_step("预期数据: ", in_data[YAMLDate.ASSERT.value])
pytest.skip()
def pytest_terminal_summary(terminalreporter):
"""
收集测试结果
"""
_PASSED = len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown'])
_ERROR = len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown'])
_FAILED = len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown'])
_SKIPPED = len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown'])
_TOTAL = terminalreporter._numcollected
_TIMES = time.time() - terminalreporter._sessionstarttime
INFO.logger.info(f"成功用例数: {_PASSED}")
ERROR.logger.error(f"异常用例数: {_ERROR}")
ERROR.logger.error(f"失败用例数: {_FAILED}")
WARNING.logger.warning(f"跳过用例数: {_SKIPPED}")
INFO.logger.info("用例执行时长: %.2f" % _TIMES + " s")
try:
_RATE = round((_PASSED + _SKIPPED) / _TOTAL * 100, 2)
INFO.logger.info("用例成功率: %.2f" % _RATE + " %")
except ZeroDivisionError:
INFO.logger.info("用例成功率: 0.00 %")

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-03-16 13:07:13
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from tools.yamlControl import GetCaseData
from lib.test_demo.DateDemo import DateDemo
from tools.assertControl import Assert
TestData = GetCaseData(ConfigHandler.merchant_data_path + r'test_demo\DateDemo.yaml').get_yaml_case_data()
@allure.epic("这里是测试平台名称")
@allure.feature("这里是测试模块名称")
class TestDateDemo:
@allure.story("这是一个测试的demo接口")
@pytest.mark.parametrize('data', TestData)
def test_date_demo(self, date):
"""
测试接口
:param :
:return:
"""
res = DateDemo().dateDemo(date)
Assert(date['resp']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_DateDemo.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning', "--reruns=2",
"--reruns-delay=2"])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'uplpad_file_test/up_files.yaml').case_process()
@allure.epic("婚奢汇")
@allure.feature("文件上传")
class TestUpFiles:
@allure.story("上传excel文件")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_up_files(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_up_files.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022-04-07 21:03:43
# @Author : 余少琪
import allure
import pytest
from config.setting import ConfigHandler
from utils.readFilesUtils.get_yaml_data_analysis import CaseData
from utils.assertUtils.assertControl import Assert
from utils.requestsUtils.requestControl import RequestControl
TestData = CaseData(ConfigHandler.data_path + r'uplpad_file_test/upload_files.yaml').case_process()
@allure.epic("婚奢汇")
@allure.feature("文件上传")
class TestUploadFiles:
@allure.story("上传excel文件")
@pytest.mark.parametrize('in_data', TestData, ids=[i['detail'] for i in TestData])
def test_upload_files(self, in_data, case_skip):
"""
:param :
:return:
"""
res = RequestControl().http_request(in_data)
Assert(in_data['assert']).assert_equality(response_data=res[0], sql_data=res[1])
if __name__ == '__main__':
pytest.main(['test_upload_files.py', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning'])

View File

@ -1,159 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/14 22:06
# @Author : 余少琪
import allure
import json
import datetime
from config.setting import get_current_system, ConfigHandler
from tools.yamlControl import GetYamlData
def allure_step(step: str, var: str) -> None:
"""
:param step: 步骤及附件名称
:param var: 附件内容
"""
with allure.step(step):
allure.attach(
json.dumps(
str(var),
ensure_ascii=False,
indent=4),
step,
allure.attachment_type.JSON)
def allure_step_no(step: str):
"""
无附件的操作步骤
:param step: 步骤名称
:return:
"""
with allure.step(step):
pass
def slash():
# 判断系统路径
SLASH = '\\'
# 判断当前操作系统
if get_current_system() == 'Linux' or get_current_system() == "Darwin":
SLASH = '/'
return SLASH
def SqlSwitch() -> bool:
"""获取数据库开关"""
switch = GetYamlData(ConfigHandler.config_path) \
.get_yaml_data()['MySqlDB']["switch"]
return switch
def getNotificationType():
# 获取报告通知类型,是发送钉钉还是企业邮箱
Date = GetYamlData(ConfigHandler.config_path).get_yaml_data()['NotificationType']
return Date
def writePageFiles(classTitle, funcTitle, caseDetail, casePath, yamlPath):
"""
自动写成 py 文件
:param yamlPath:
:param casePath: 生成的py文件地址
:param classTitle: 类名称, 读取用例中的 caseTitle 作为类名称
:param funcTitle: 函数名称 caseTitle首字母小写
:param caseDetail: 函数描述读取用例中的描述内容做为函数描述
:return:
"""
Author = GetYamlData(ConfigHandler.config_path).get_yaml_data()['TestName']
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
page = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : {now}
# @Author : {Author}
from tools.requestControl import RequestControl
from tools.yamlControl import GetCaseData
from config.setting import ConfigHandler
class {classTitle}(object):
@staticmethod
def {funcTitle}(inData):
"""
{caseDetail}
:param inData:
:return:
"""
resp = RequestControl().HttpRequest(inData['method'], inData)
return resp
if __name__ == '__main__':
path = GetCaseData(ConfigHandler.data_path + r'{yamlPath}').get_yaml_case_data()[0]
data = {classTitle}().{funcTitle}(path)
print(data)
'''
with open(casePath, 'w', encoding="utf-8") as f:
f.write(page)
def writeTestCaseFile(allureEpic, allureFeature, classTitle, funcTitle, caseDetail, casePath, yamlPath, fileName, PackagePath):
"""
:param allureEpic: 项目名称
:param allureFeature: 模块名称
:param classTitle: 类名称
:param funcTitle: 函数名称
:param caseDetail: 用例描述
:param casePath: case 路径
:param yamlPath: yaml 文件路径
:return:
"""
Author = GetYamlData(ConfigHandler.config_path).get_yaml_data()['TestName']
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
page = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : {now}
# @Author : {Author}
import allure
import pytest
from config.setting import ConfigHandler
from tools.yamlControl import GetCaseData
{PackagePath}
from tools.assertControl import Assert
TestData = GetCaseData(ConfigHandler.merchant_data_path + r'{yamlPath}').get_yaml_case_data()
@allure.epic("{allureEpic}")
@allure.feature("{allureFeature}")
class Test{classTitle}:
@allure.story("这是一个测试的demo接口")
@pytest.mark.parametrize('inData', TestData)
def test_{funcTitle}(self, inData):
"""
{caseDetail}
:param :
:return:
"""
res = {classTitle}().{funcTitle}(inData)
Assert(inData['resp']).assertEquality(responseData=res[0], sqlData=res[1])
if __name__ == '__main__':
pytest.main(['{fileName}', '-s', '-W', 'ignore:Module already imported:pytest.PytestWarning', "--reruns=2", "--reruns-delay=2"])
'''
with open(casePath, 'w', encoding="utf-8") as f:
f.write(page)

Some files were not shown because too many files have changed in this diff Show More