![]() Merge pull request !13 from cc1986746916/dev |
||
---|---|---|
common | ||
config | ||
pic | ||
test_suite | ||
util | ||
.gitignore | ||
README.md | ||
conftest.py | ||
pytest.ini | ||
requirements.txt | ||
setupMain.py |
README.md
接口自动化测试框架 pytest+ymal+allure+requests+redis
联系方式
QQ 529548204
邮箱 529548204@qq.com
如果有问题请联系我 大家共同学习共同进步~
更新日志
2022年6月7日 更新 取消config中http类型 改为yaml数据内
2022年5月30日 更新 YAML读取时间格式datetime数据后 json处理方式
2022年5月20日 更新增加jenkins持续集成说明配置方法等
2022年5月17日 更新增加xml参数处理,增加自定义加密方式(加密规则需要自己写 需要一定编码能力)
前言
环境要求
python 3.9
redis (存放用例数据)
mysql (测试环境数据库)
jdk
allure
持续集成:
docker
jenkins
框架采用python的pytest模块, 搭配requests以及allure测试报告,可以发送钉钉通知邮件通知, 支持自定义接口加密,util.tools.encryption文件内自己编辑加密规则, 可以根据yaml测试数据自动生成用例, 支持接口关联, 支持类似jmeter的函数助手, 可以通过脚本进行接口录制,辅助编写yaml测试数据文件, 支持数据库断言, 支持分布式,jenkins持续集成。
注意
单用例调试时需要执行一次util\tools\readYamlFile.py将测试数据存入redis
一、目录结构
|--接口自动化测试框架 # 主目录
├─ common # 封装断言以及requests的方法
├─ caches # 本地缓存保存路径
├─ config # 配置文件读取
└─ config.ini
├─ testsuite # 测试相关文件
├─ datas #测试数据
└─ 项目文件夹 名称同config中 testname一致 # 可以通过newproject脚本生成
└─ login.yml # 用例数据 格式参考下面YAML PARAM格式说明
├─ testcase
└─ 项目文件夹 名称同config中 testname一致 # 测试用例 可以通过writepage脚本生成
└─ test_login.py
└─recording # 录制脚本文件夹放录制的接口文档
├─ util # 常用工具 用例生成 接口录制
├─ tools # 内部调用工具方法包含yaml读取 函数助手 数据库链接等
└─ scripts # 包含生成新项目,自动生成用例,接口录制
├─ log # 日志
├─ report # allure测试报告
├─ pytest.ini # pytest配置
├─ requirements.txt
├─ README.md
└─ setupMain.py # 整体执行程序。
二、关键文件介绍
1.yaml测试数据格式
1.1 本地缓存介绍
保存本地缓存方法为3种
1. body 请求体内的参数中保存:
body如果是 "id=2&path=haha" 会转换成字典 然后根据path使用jsonpath取值
2. response : 从json格式的响应结果中获取
3. cookies: 保存响应结果的cooies到本地
读取本地缓存方法
将需要替换为缓存数据的内容改成:$caches(cookies)$
示例:
cache: # 本地缓存
- cachefrom: 'body'
path: '$.code'
name: 'code'
# 使用方法 需要的地方替换为$caches(code)$
- cachefrom: 'response'
path: '$.data'
name: 'data'
# 使用方法 需要的地方替换为$caches(data)$
- cachefrom: 'cookies'
path: # cookies 时path为空
name: 'cookies'
# 使用方法 需要的地方替换为$caches(cookies)$
1.2 接口关联介绍
通过relevance字段来判断是否需要关联 如果不需要relevance字段为空即可
示例:
下面示例为 关联接口为tradeAdd 与 tradeAdd2两条yaml数据中case的第一条和第二条
分别去这两个接口返回结果的 id 并命名为tradeId 、tradeId2
内存中会保存成字典格式
{"tradeId":"10","tradeId2":"11"}
读取方法:$relevance(tradeId2)$
relevance:
response:
- relCaseName: shangchuan # 其他testcase的ID
relCaseNum: 1 # 关联的case数组里 第几条数据
reldata:
- value: $.data.fileName # 当前返回结果的jsonpath
name: fileName # 关联值名称
- value: $.data.policy # 当前返回结果的jsonpath
name: policy # 关联值名称
- value: $.data.signature # 当前返回结果的jsonpath
name: signature # 关联值名称
- relCaseName: tradeAdd2 # 其他testcase的ID
relCaseNum: 2 # 关联的case数组里 第几条数据
reldata:
- value: $.data.fileName # 当前返回结果的jsonpath
name: fileName # 关联值名称
- value: $.data.policy # 当前返回结果的jsonpath
name: policy # 关联值名称
- value: $.data.signature # 当前返回结果的jsonpath
name: signature # 关联值名称
1.3 参数介绍
file
: 通过case外关键字file判断是否需要上传文件 如果需要则格式为:{上传文件的参数名:文件路径}
param
:包含两种请求格式
(1)json格式 :
{ "username": "finsiot","password": "$caches(pwd)$" # 读取缓存值 }
(同样适用于xml格式 会根据请求头application/xml或者text/xml 将字典转换成xml类型)
(2)param格式
username=admin&password=123
urlparam
为路径参数:{ id: 123 }
会根据字典转换成
路径参数 address中 v1/api/$url(id)$/
中会根据id替换为123
data:
file: {
files: D:\test\test.csv # 上传文件的参数名:文件路径
}
param: {
"username": "finsiot","password": "$caches(pwd)$" # 读取缓存值
}
urlparam: {
id: 123
}# 路径参数 v1/api/$url(id)$/
1.4 断言介绍
jsonpath
json格式数据断言:
根据json路径格式来获取实际结果同value中的预期结果进行判断
sqlassert
数据库结果断言
可同时判断多个结果值
根据sql中的查询语句查询出来的 第一条结果进行判断
time
响应时间断言 默认为2秒
code
http响应码断言
assert:
jsonpath:
- {
"path": "$.data.expense_trend[0].peak_hour.peak_hour",
"value": "123", # 预期结果
"asserttype": "==" # 判断相等
}
- {
"path": "$.code",
"value": 0,
"asserttype": "=="
}
- {
"path": "$.data.id",
"value": 196,
"asserttype": "=="
}
sqlassert:
# 如果不需要 此字段置空即可
- {
"datas": [
{
"path": "$.data.id",
"name": "id"
},
{
"path": "$.data.username",
"name": "username"
},
],
"sql": "select * from saas.user where username = '****'",
# 取数据库查询出的第一条数据进行验证 如果存在 列名 username 值为$.data.username则通过
"db_name": "database" # 判断链接那个数据库
}
time: 2 # 响应时间断言
code: 200
1.5 生成随机数据介绍
部分数据采用faker库生成
int_num = "$RandomPosInt(1,333)$" # 267
str_num = '$RandomString($RandomPosInt(2,23)$)$$RandomPosInt(1,333)$' # AbE3c14580f29aDFe5
float_num = '$RandomFloat($RandomPosInt(2,13)$,$RandomPosInt(2,13)$,$RandomPosInt(2,13)$)$' # 11.84864
time_num = '$GetTime(time_type=else,layout=%Y-%m-%d %H:%M:%S,unit=0,0,0,0,0)$' # 当前时间 2022-04-14 13:27:01
time_num2 = '$GetTime(time_type=future,layout=%Y-%m-%d %H:%M:%S,unit=0,0,0,3,0)$' # 未来时间3天后 2022-04-17 13:32:42
time_num3 = '$GetTime(time_type=past,layout=%Y-%m-%d %H:%M:%S,unit=0,0,0,3,0)$' # 过去时间3天前 2022-04-11 13:33:24
choice_num = '$Choice($RandomPosInt(2,13)$)$' # 6
email = "$faker(email)$"# 邮箱 # xkong@example.net
idcard = "$faker(idcard)$" # 130802196003197594
province = "$faker(province)$" # 新疆维吾尔自治区
city = "$faker(city)$" # 鹏市
phone_number = "$faker(phone_number)$" # 15070673645
name = "$faker(name)$" # 许云
1.6 后置请求处理
使用场景: 新增数据A之后一系列用例执行完需要后置来还原数据形成业务闭环.
datatype 有3种类型 param json 和urlparam
dataname为后置关联的参数中参数名称
path为当前请求返回结果的jsonpath 根据path的值更换后置处理参数值
如果不需要后置处理 teardown 置空即可
teardown:
- tdName: pointDel # 其他testcase的ID
tdNum: 1 # 关联的case数组里 第几条数据
tddata:
- datatype: param
dataname: name
path: $.name
- datatype: urlparam
dataname: pointId
path: $.id
- tdName: pointAdd # 其他testcase的ID
tdNum: 1 # 关联的case数组里 第几条数据
tddata:
- datatype: json
dataname: name
path: $.name
- datatype: urlparam
dataname: pointId
path: $.id
三、文件展示
3.1yaml文件展示
!!!所有case的id 务必唯一!!!
name: "登录"
# 测试用例模块名称
token: Authorization
# 判断此接口是否使用token false 或者"cookie"或者"Authorization"等 如果不需要 置空或者填写false
order: 1
用例执行顺序 @pytest.mark.run(order=1) # 因为此功能不支持分布式录制中已取消file
: bool值判断 true为此接口需要上传文件参数
case
: 测试用例数据
login: # caseID **请务必唯一**
name: "登录" #测试用例模块
token: false # 判断此接口是否使用token false 或者"cookie"或者"Authorization"等
# token: "Authorization"
order: 1 # 用例执行顺序 @pytest.mark.run(order=1) 因为此功能不支持分布式录制中已取消
file: true # bool值 true为需要文件的接口
case:
- info: "用户名登录-成功" # 用例信息
http: https # http
host: 'host' # config.ini里面 请求host的key
address: '/v1/apps/$url(region_id)$/' # $url(region_id)$ 正则匹配参数中的路径参数
method: 'post'
cache: # 本地缓存
# 保存本地缓存方法为3种
# 1. body 请求体内的参数中保存:
# body如果是 "id=2&path=haha" 会转换成字典 然后根据path使用jsonpath取值
# 2. response : 从json格式的响应结果中获取
# 3. cookies: 保存响应结果的cooies到本地
- cachefrom: 'body'
path: '$.code'
name: 'code'
# 使用方法 需要的地方替换为$caches(code)$
- cachefrom: 'response'
path: '$.data'
name: 'data'
# 使用方法 需要的地方替换为$caches(data)$
- cachefrom: 'cookies'
path: # cookies 时path为空
name: 'cookies'
# 使用方法 需要的地方替换为$caches(cookies)$
# 接口关联
relevance:
# 判断如果不需要关联relevance字段为空即可
# 如果需要关联就
response:
# 场景 接口A删除请求,需要接口B新增请求中返回的ID以及name
- relCaseName: pointAdd # 其他testcase的ID
relCaseNum: 1 # 关联的case数组里 第几条数据
reldata:
- value: $.id # 当前返回结果的jsonpath
name: pointId # 关联值名称
- value: $.name # 当前返回结果的jsonpath
name: pointname # 关联值名称
teardown:
- tdName: pointDel # 后置请求的caseID
tdNum: 1 # 后置请求的case数组里 第几条数据
tddata:
- datatype: param
dataname: name
path: $.name
- datatype: urlparam
dataname: pointId
path: $.id
- datatype: json
dataname: name
path: $.name
# 机制为 根据relevance字段生成字典{"tradeId":"123"}
# 使用关联值方法为将需要替换的地方修改为 $relevance(tradeId)$
headers: {
"Content-Type": "application/json"
}
data:
file: {
files: D:\test\test.csv # 上传文件的参数名:文件路径
}
param: {
"username": "finsiot","password": "$caches(pwd)$" # 读取缓存值
}
urlparam: {
id: 123
}# 路径参数 v1/api/$url(id)$/
assert:
jsonpath:
- {
"path": "$.data.expense_trend[0].peak_hour.peak_hour",
"value": "123",
"asserttype": "=="
}
- {
"path": "$.code",
"value": 0,
"asserttype": "=="
}
- {
"path": "$.data.id",
"value": 196,
"asserttype": "=="
}
sqlassert:
# 如果不需要 此字段置空即可
- {
"datas": [
{
"path": "$.data.id",
"name": "id"
},
{
"path": "$.data.username",
"name": "username"
},
],
"sql": "select * from saas.user where username = '****'",
# 取数据库查询出的第一条数据进行验证 如果存在 列名 username 值为$.data.username则通过
"db_name": "database" # 判断链接那个数据库
}
time: 2 # 响应时间断言
code: 200 # HTTP响应码断言
3.2 config.ini配置文件格式
重点为[directory] 中的test_name 所有程序都围绕test_name 进行执行 根据testname来生成测试用例执行测试等
[directory] # 路径相关
log_dir = /logs
data_dir = /datas
page_dir = /page
report_xml_dir = /report/xml
report_html_dir = /report/html
test_suite = /test_suite
case_dir = /testcase
cache_dir = /caches
test_name = 测试项目名称: saasWeb
[host]
host =
[email]
;服务器
mail_host = smtp.sina.com
;发送邮箱
mail_user =
;口令
mail_pass =
;发送者
sender =
;接收邮箱
receivers =
[database]
host = 192.
port = 3306
user = root
password =
;database =
database =
charset = utf8
[redis]
host = 127.0.0.1
port = 6379
db = 1
password = 123456
charset = UTF-8
[dingding]
webhook =
secret =
四、接口录制
1.代理设置
设置计算机代理
http=127.0.0.1:4444;https=127.0.0.1:4444;ftp=127.0.0.1:4444
<-loopback>
HTTPS:
http://mitm.it/ 开启代理后下载证书安装
执行脚本 recording.py 每个请求将会在 ./test_suite/recording 文件夹中建立文件 每次执行录制时会覆盖原文件
五、接口加密(测试版)
加签加密法:
在config中encryption
增加sign的值 为签
需要在util.tools.encryption文件内自己编辑加密规则
需要加密的接口 YAML数据中encryption的值为true 会根据此在请求用例前增加加密装饰器
六、操作方法
- 新建config/config.ini文件 格式如上例子
- 执行util/scripts/newProject.py 根据testname生成测试项目基础目录
- 在生成的test_suite/datas/testname 文件夹下增加yaml测试用例
- 执行util/scripts/writeCase.py生成测试脚本 关于token 需要根据自己项目情况修改yaml文件中token关键字 如果不需要token值为false 需要token则改为需要的类型
- 执行setupMain.py开始测试(单用例调试时需要执行util\tools\readYamlFile.py将测试数据存入redis)
七、代码展示
test_login.py
login.yml
conftest.py
八、jenkins集成
8.1 docker环境搭建:
1 安装docker(不会请百度)
2 新建任意文件夹
3 新建文件 Dockerfile
4 新建文件docker-compose.yml
5 复制python安装包
文件内容如下:
docker-compose.yml
version: '3'
services:
jenkins:
image: "jenkins_python"
build: .
depends_on:
- redis
container_name: jenkins
volumes:
- "/home/jenkins:/var/jenkins_home"
ports:
- "10240:8080"
- "10241:50000"
user: root
links:
- redis
redis:
image: "redis:alpine"
container_name: redis
restart: always
volumes:
- ../redis/redis.conf:/usr/local/etc/redis/redis.conf:rw
- ../redis/data:/data:rw
command: --requirepass "123456"
ports:
- "6379:6379"
expose:
- 6379
Dockerfile
FROM jenkins/jenkins
USER root
ADD ./Python-3.9.7.tgz /var/python
RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list
RUN sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list
WORKDIR /var/python3
RUN apt-get update \
&& apt-get install -y vim\
&& apt-get -y install gcc automake autoconf libtool make \
&& apt-get -y install zlib* \
&& apt-get -y install openssl libssl-dev \
&& apt-get -y install sudo \
&& apt-get install libffi-dev \
&& apt-get install -y nodejs \
&& /var/python/Python-3.9.7/configure --prefix=/var/python3 --with-ssl \
&& make \
&& make install \
&& ln -sf /var/python3/bin/python3.9 /usr/bin/python \
&& ln -sf /var/python3/bin/pip3 /usr/bin/pip \
&& apt install npm -y \
&& npm install -g allure-commandline --save-dev \
&& /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
WORKDIR /
在文件目录内执行命令
docker-compose up
这样生成了两个容器 一个是包含allure和python的jenkins容器和一个redis容器
8.2jenkins配置
Javahome路径 查询echo $JAVA_HOME 输出 /opt/java/openjdk
1 安装allure 和gitee插件
2 配置全局变量
3.新建autotest任务
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=1000 -r requirements.txt
python setupMain.py
5 增加构建后操作allure报告
report/xml
点击高级
report/html
注意路径 相对workspace内任务的路径 前面没有斜线
###8.3注意事项
注意路径 相对workspace内任务的路径 前面没有斜线
Ubuntu文件编码有坑 项目yaml文档必须用utf-8编码
8.4脚本代码更改
找到setipMain.py
红框内注意为当前jenkins建立的任务名称
修改config.ini内redis配置 配置内容与docker容器保持一致 如密码
注意 host 为dockercompose内link名称 内部链接采用redis:6379
总结
这里给大家分享出来希望能和朋友们共同学习,共同进步 源码地址 https://gitee.com/a529548204/apitest