mirror of https://gitee.com/a529548204/apitest.git
增加后置请求功能详情见使用说明
This commit is contained in:
parent
7367243e1b
commit
bd6036b2f0
69
README.md
69
README.md
|
@ -88,6 +88,9 @@ QQ 529548204
|
|||
分别去这两个接口返回结果的 id 并命名为tradeId 、tradeId2
|
||||
内存中会保存成字典格式
|
||||
`{"tradeId":"10","tradeId2":"11"}`
|
||||
读取方法:`$relevance(tradeId2)$`
|
||||
|
||||

|
||||
|
||||
```yaml
|
||||
relevance:
|
||||
|
@ -178,8 +181,7 @@ QQ 529548204
|
|||
### 1.4 生成随机数据介绍
|
||||
部分数据采用faker库生成
|
||||
```python
|
||||
int_num = "$RandomInt(1,333)$" # 267 int格式 不可和字符串拼接
|
||||
int_num2 = "$RandomPosInt(1,333)$" # '267' 字符串格式
|
||||
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
|
||||
|
@ -193,7 +195,37 @@ QQ 529548204
|
|||
phone_number = "$faker(phone_number)$" # 15070673645
|
||||
name = "$faker(name)$" # 许云
|
||||
```
|
||||
### 1.5 整体yaml展示
|
||||
|
||||
### 1.4 后置请求处理
|
||||
使用场景: 新增数据A之后一系列用例执行完需要后置来还原数据形成业务闭环.
|
||||
datatype 有3种类型 param json 和urlparam
|
||||
dataname为后置关联的参数中参数名称
|
||||
path为当前请求返回结果的jsonpath 根据path的值更换后置处理参数值
|
||||
如果不需要后置处理 teardown 置空即可
|
||||
```yaml
|
||||
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 务必唯一***!!!
|
||||
|
@ -241,11 +273,28 @@ login: # caseID **请务必唯一**
|
|||
relevance:
|
||||
# 判断如果不需要关联relevance字段为空即可
|
||||
# 如果需要关联就
|
||||
- relCaseName: tradeAdd # 其他testcase的ID
|
||||
relCaseNum: 1 # 关联的case数组里 第几条数据
|
||||
value: $.id # 当前返回结果的jsonpath
|
||||
name: tradeId # 关联值名称
|
||||
|
||||
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)$
|
||||
|
@ -343,13 +392,13 @@ charset = utf8
|
|||
webhook =
|
||||
secret =
|
||||
```
|
||||
# 三、操作方法
|
||||
# 四、操作方法
|
||||
1. 新建config/config.ini文件 格式如上例子
|
||||
2. 执行**util/scripts/newProject.py** 根据testname生成测试项目基础目录
|
||||
3. 在生成的**test_suite/datas/*testname*** 文件夹下增加yaml测试用例
|
||||
4. 执行**util/scripts/writeCase.py**生成测试脚本 关于token 需要根据自己项目情况修改yaml文件中token关键字 如果不需要token值为false 需要token则改为需要的类型
|
||||
5. 执行**setupMain.py**开始测试
|
||||
# 四、代码展示
|
||||
# 五、代码展示
|
||||
test_login.py
|
||||

|
||||
login.yml
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.9 KiB |
|
@ -22,3 +22,4 @@ shoucang:
|
|||
method: POST
|
||||
cache:
|
||||
relevance:
|
||||
teardown:
|
|
@ -25,3 +25,4 @@ login:
|
|||
path: # body如果是 "id=2&path=haha" 会转换成字典 然后根据path使用jsonpath取值
|
||||
name: 'cookie'
|
||||
relevance:
|
||||
teardown:
|
||||
|
|
|
@ -10,6 +10,8 @@ from util.tools.log import Log
|
|||
from util.tools.readYamlFile import ini_allyaml
|
||||
from common.basePage import apisend
|
||||
from util.tools.iniRequests import relevance
|
||||
from util.tools.iniHeaders import iniheaders
|
||||
from util.tools.requestsTearDown import caseTearDown
|
||||
alldata = ini_allyaml()
|
||||
|
||||
Log()
|
||||
|
@ -24,4 +26,6 @@ __all__ = [
|
|||
'apisend',
|
||||
'alldata',
|
||||
'relevance',
|
||||
'iniheaders',
|
||||
'caseTearDown',
|
||||
]
|
|
@ -35,6 +35,8 @@ from util.tools.log import Log
|
|||
from util.tools.readYamlFile import ini_allyaml
|
||||
from common.basePage import apisend
|
||||
from util.tools.iniRequests import relevance
|
||||
from util.tools.iniHeaders import iniheaders
|
||||
from util.tools.requestsTearDown import caseTearDown
|
||||
alldata = ini_allyaml()
|
||||
|
||||
Log()
|
||||
|
@ -49,6 +51,8 @@ __all__ = [
|
|||
'apisend',
|
||||
'alldata',
|
||||
'relevance',
|
||||
'iniheaders',
|
||||
'caseTearDown',
|
||||
]""")
|
||||
if "conftest.py" not in os.listdir(casepath):
|
||||
with open(casepath + "/" + r"{}".format("conftest.py"), 'w', encoding='utf-8') as f:
|
||||
|
|
|
@ -66,6 +66,7 @@ class Counter:
|
|||
case["host"] = "host" # 可参数化
|
||||
case["address"] = url_param_list[0] # path
|
||||
case["relevance"] = None
|
||||
case["teardown"] = None
|
||||
case["cache"] = None
|
||||
case["headers"] = {}
|
||||
case["headers"] = dict(headers)
|
||||
|
|
|
@ -50,12 +50,14 @@ class Test_{filename}(object):""")
|
|||
# 判断是否需要token 默认类型是 Authorization
|
||||
f.write(f"""
|
||||
def test_{item}(self, casedata, setup_Login):
|
||||
casedata["headers"] = iniheaders(casedata["headers"])
|
||||
casedata["headers"]["{filedata[item]["token"]}"] = "$caches(cookie)$"
|
||||
casedata = relevance(alldata, casedata, setup_Login)""")
|
||||
elif filedata[item]["token"] and filedata[item]["token"] != "Cookies":
|
||||
# 判断是否需要token 默认类型是 Authorization
|
||||
f.write(f"""
|
||||
def test_{item}(self, casedata, setup_Login):
|
||||
casedata["headers"] = iniheaders(casedata["headers"])
|
||||
casedata["headers"]["{filedata[item]["token"]}"] = setup_Login
|
||||
casedata = relevance(alldata, casedata, setup_Login)""")
|
||||
else:
|
||||
|
@ -66,15 +68,29 @@ class Test_{filename}(object):""")
|
|||
f.write("""
|
||||
res,restime, code = apisend(host=casedata["host"], address=casedata["address"], method=casedata["method"],
|
||||
headers=casedata["headers"],
|
||||
data=casedata["data"], caches=casedata["cache"])
|
||||
data=casedata["data"], caches=casedata["cache"])""")
|
||||
if not filedata[item]["token"]:
|
||||
f.write("""
|
||||
caseTearDown(alldata,casedata,res)
|
||||
asserting(hope_res=casedata["assert"], real_res=res, re_time=restime, re_code=code)\n""")
|
||||
else:
|
||||
f.write("""
|
||||
caseTearDown(alldata,casedata,res,setup_Login)
|
||||
asserting(hope_res=casedata["assert"], real_res=res, re_time=restime, re_code=code)\n""")
|
||||
else:
|
||||
f.write(f"""
|
||||
res,restime, code = apisend(host=casedata["host"], address=casedata["address"], method=casedata["method"],
|
||||
headers=casedata["headers"],
|
||||
data=casedata["data"], caches=casedata["cache"],
|
||||
files=casedata["data"]["file"])
|
||||
asserting(hope_res=casedata["assert"], real_res=res, re_time=restime, re_code=code)\n""")
|
||||
files=casedata["data"]["file"])""")
|
||||
if not filedata[item]["token"]:
|
||||
f.write("""
|
||||
caseTearDown(alldata,casedata,res)
|
||||
asserting(hope_res=casedata["assert"], real_res=res, re_time=restime, re_code=code)\n""")
|
||||
else:
|
||||
f.write("""
|
||||
caseTearDown(alldata,casedata,res,setup_Login)
|
||||
asserting(hope_res=casedata["assert"], real_res=res, re_time=restime, re_code=code)\n""")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -10,6 +10,8 @@ import os
|
|||
import shutil
|
||||
|
||||
import jsonpath
|
||||
|
||||
from util.tools.datasTypeChange import valueHandle, strHandleCookies
|
||||
from util.tools.mkDir import mk_dir
|
||||
from util.tools import root_path
|
||||
|
||||
|
@ -25,30 +27,6 @@ body: get请求 根据name replace参数 post 根据path 更新json数据
|
|||
cachepath = root_path + dir_manage("${cache_dir}$")
|
||||
|
||||
|
||||
def valueHandle(data: str):
|
||||
"""
|
||||
字符串转化成字典
|
||||
url的param参数转化为字典
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
param_dict = {}
|
||||
param_list = data.split("&")
|
||||
for param in param_list:
|
||||
param_dict[param.split("=")[0]] = param.split("=")[1]
|
||||
return param_dict
|
||||
|
||||
|
||||
def strHandle(data: dict):
|
||||
"""
|
||||
字典转化成字符串
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
cookies_str = ''
|
||||
for k, v in data.items():
|
||||
cookies_str = cookies_str + (k + "=" + v + ";")
|
||||
return cookies_str
|
||||
|
||||
|
||||
class Cache(object):
|
||||
|
@ -156,7 +134,7 @@ class Cache(object):
|
|||
# values = jsonpath.jsonpath(param, data['path'])
|
||||
# if not values:
|
||||
# raise ValueError("path错误")
|
||||
cookie = strHandle(cookie)
|
||||
cookie = strHandleCookies(cookie)
|
||||
self.set(key=data["name"], value=cookie)
|
||||
# else:
|
||||
# values = jsonpath.jsonpath(valueHandle(param), data['path'])
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# coding:utf-8
|
||||
"""
|
||||
@author: jing
|
||||
@contact: 529548204@qq.com
|
||||
@file: datasTypeChange.py
|
||||
@time: 2022/5/11 15:05
|
||||
"""
|
||||
|
||||
|
||||
def valueHandle(data: str):
|
||||
"""
|
||||
url格式字符串转化成字典
|
||||
url的param参数转化为字典
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
param_dict = {}
|
||||
param_list = data.split("&")
|
||||
for param in param_list:
|
||||
param_dict[param.split("=")[0]] = param.split("=")[1]
|
||||
return param_dict
|
||||
|
||||
|
||||
def strHandleCookies(data: dict):
|
||||
"""
|
||||
字典转化成字符串
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
cookies_str = ''
|
||||
for k, v in data.items():
|
||||
cookies_str = cookies_str + (k + "=" + str(v) + ";")
|
||||
return cookies_str
|
||||
|
||||
|
||||
def strHandleUrl(data: dict):
|
||||
"""
|
||||
字典转化成字符串
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
url_str = ''
|
||||
for k, v in data.items():
|
||||
url_str = url_str + (k + "=" + str(v) + "&")
|
||||
return url_str.strip("&")
|
||||
|
||||
if __name__ == '__main__':
|
||||
d = {
|
||||
"a":1,
|
||||
"b":2
|
||||
}
|
||||
print(strHandleUrl(d))
|
|
@ -0,0 +1,17 @@
|
|||
# coding:utf-8
|
||||
"""
|
||||
@author: jing
|
||||
@contact: 529548204@qq.com
|
||||
@file: iniHeaders.py
|
||||
@time: 2022/5/11 10:56
|
||||
"""
|
||||
|
||||
def iniheaders(headers):
|
||||
"""
|
||||
处理请求头特殊情况 如果请求头为空 读取后并不是字典格式 所以需要处理
|
||||
:param headers:
|
||||
:return:
|
||||
"""
|
||||
if not headers:
|
||||
headers={}
|
||||
return headers
|
|
@ -5,11 +5,17 @@
|
|||
@file: iniRequests.py
|
||||
@time: 2022/4/2 16:48
|
||||
"""
|
||||
import logging
|
||||
|
||||
from common.basePage import apisend
|
||||
from util.tools.iniHeaders import iniheaders
|
||||
from util.tools.randomData import replace_random
|
||||
from util.tools.log import Log
|
||||
import jsonpath
|
||||
import allure
|
||||
|
||||
Log()
|
||||
|
||||
|
||||
def relevance(alldata, relevancedata, headerdata=None):
|
||||
"""
|
||||
|
@ -19,31 +25,38 @@ def relevance(alldata, relevancedata, headerdata=None):
|
|||
:param headerdata: 被关联接口可能需要的请求头
|
||||
:return:
|
||||
"""
|
||||
reldatalist = relevancedata["relevance"]["response"]
|
||||
|
||||
reldict = {}
|
||||
if reldatalist:
|
||||
if relevancedata["relevance"]:
|
||||
reldatalist = relevancedata["relevance"]["response"]
|
||||
for reldata in reldatalist:
|
||||
try:
|
||||
relcase = alldata[reldata["relCaseName"]]["case"][reldata["relCaseNum"] - 1]
|
||||
# 获取被关联请求数据
|
||||
# if p["relevance"]:
|
||||
# relevance(alldata,relevancedata=p,headerdata=headerdata)
|
||||
with allure.step("执行关联接口"):
|
||||
if relcase["relevance"]:
|
||||
# 如果被关联请求数据仍然存在关联情况 递归
|
||||
relcase = relevance(alldata, relevancedata=relcase, headerdata=headerdata)
|
||||
if alldata[reldata["relCaseName"]]["token"]:
|
||||
# 处理请求头
|
||||
relcase["headers"] = iniheaders(relcase["headers"])
|
||||
relcase["headers"][alldata[reldata["relCaseName"]]["token"]] = headerdata
|
||||
# 进行关联接口请求
|
||||
res, time, code = apisend(address=relcase["address"],
|
||||
method=relcase["method"], headers=relcase["headers"], data=relcase["data"],
|
||||
caches=relcase["cache"], host=relcase["host"])
|
||||
for i in reldata["reldata"]:
|
||||
reldict[i["name"]] = jsonpath.jsonpath(res, i["value"])[0]
|
||||
|
||||
relcase = alldata[reldata["relCaseName"]]["case"][reldata["relCaseNum"] - 1]
|
||||
# 获取被关联请求数据
|
||||
# if p["relevance"]:
|
||||
# relevance(alldata,relevancedata=p,headerdata=headerdata)
|
||||
with allure.step("执行关联接口"):
|
||||
if relcase["relevance"]:
|
||||
# 如果被关联请求数据仍然存在关联情况 递归
|
||||
relcase = relevance(alldata, relevancedata=relcase, headerdata=headerdata)
|
||||
if alldata[reldata["relCaseName"]]["token"]:
|
||||
# 处理请求头
|
||||
relcase["headers"][alldata[reldata["relCaseName"]]["token"]] = headerdata
|
||||
# 进行关联接口请求
|
||||
res, time, code= apisend(address=relcase["address"],
|
||||
method=relcase["method"], headers=relcase["headers"], data=relcase["data"],
|
||||
caches=relcase["cache"],host=relcase["host"])
|
||||
|
||||
reldict[reldata["name"]] = jsonpath.jsonpath(res, reldata["value"])[0]
|
||||
logging.info(f"成功执行前置关联接口:{relcase['info']}")
|
||||
# 处理结果存入字典中 得到{关联数据的name:关联数据的值}
|
||||
except Exception as e:
|
||||
logging.error(f"执行前置关联接口:{relcase['info']}错误,{e}")
|
||||
raise
|
||||
|
||||
data = eval(replace_random(str(relevancedata),param=reldict))
|
||||
data = eval(replace_random(str(relevancedata), param=reldict))
|
||||
# 将当前请求根据正则匹配 例: relevancedata 中包含 $relevance(id)$ reldict={id:1} 处理后 $relevance(id)$ 被替换为1
|
||||
# 返回关联之完成 处理后的数据
|
||||
return data
|
||||
|
@ -53,7 +66,8 @@ if __name__ == '__main__':
|
|||
d = {'info': '固件详情', 'host': 'host', 'address': '/v1/device/firmware/$url(firmwareId)$/', 'method': 'get',
|
||||
'cache': None,
|
||||
'relevance': [{'relCaseName': 'firmwareList', 'relCaseNum': 1, 'value': '$.data[0].id', 'name': 'firmwareId'},
|
||||
{'relCaseName': 'firmwareList', 'relCaseNum': 1, 'value': '$.data[0].id', 'name': 'firmwareId2'}],
|
||||
{'relCaseName': 'firmwareList', 'relCaseNum': 1, 'value': '$.data[0].id',
|
||||
'name': 'firmwareId2'}],
|
||||
'headers': {'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': None},
|
||||
'data': {'param': None, 'urlparam': {'firmwareId': '$relevance(firmwareId)$'}}, 'assert': {
|
||||
'jsonpath': [{'path': '$.msg', 'value': 'Success.', 'asserttype': '==', 'relevanceCheck': None},
|
||||
|
|
|
@ -26,6 +26,8 @@ def ini_allyaml():
|
|||
for k,v in file_data.items():
|
||||
if k in alldata:
|
||||
raise Exception("存在重复case")
|
||||
if not file_data["headers"]:
|
||||
file_data["headers"] = {}
|
||||
alldata= {**alldata,**file_data}
|
||||
except UnicodeDecodeError:
|
||||
with open(file, 'r') as f:
|
||||
|
@ -55,7 +57,7 @@ if __name__ == '__main__':
|
|||
# print(datapath)
|
||||
# get_yaml_data(r"F:\api2.0\config\runConfig.yml")
|
||||
d = ini_allyaml()
|
||||
print(type(d))
|
||||
print(d)
|
||||
# # case_level = runConfig_dict[0]["address"].format(**{"home_id": "123"})
|
||||
# print(runConfig_dict)
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# coding:utf-8
|
||||
"""
|
||||
@author: jing
|
||||
@contact: 529548204@qq.com
|
||||
@file: requestsTearDown.py
|
||||
@time: 2022/5/11 13:28
|
||||
"""
|
||||
import logging
|
||||
|
||||
from common.basePage import apisend
|
||||
from util.tools.iniHeaders import iniheaders
|
||||
from util.tools.datasTypeChange import strHandleUrl, valueHandle
|
||||
from util.tools.log import Log
|
||||
import jsonpath
|
||||
import allure
|
||||
|
||||
Log()
|
||||
|
||||
|
||||
def caseTearDown(alldata, casedata, caseres, headerdata=None):
|
||||
"""
|
||||
使用场景: 新增数据A之后一系列用例执行完需要后置来还原数据形成业务闭环.
|
||||
datatype 有3种类型 param json 和urlparam
|
||||
dataname为后置关联的参数中参数名称
|
||||
path为当前请求返回结果的jsonpath 根据path的值更换后置处理参数值
|
||||
如果不需要后置处理 teardown 置空即可
|
||||
:param alldata: 全部数据
|
||||
:param casedata: 测试数据
|
||||
:param caseres: 当前用例返回结果
|
||||
:param headerdata: 请求头数据
|
||||
:return:
|
||||
"""
|
||||
teardowndata = casedata["teardown"]
|
||||
if teardowndata:
|
||||
for req in teardowndata:
|
||||
tdcase = alldata[req["tdName"]]["case"][req["tdNum"] - 1]
|
||||
try:
|
||||
with allure.step("执行后置接口"):
|
||||
if alldata[req["tdName"]]["token"]:
|
||||
# 处理请求头
|
||||
tdcase["headers"] = iniheaders(tdcase["headers"])
|
||||
tdcase["headers"][alldata[req["tdName"]]["token"]] = headerdata
|
||||
for tddata in req["tddata"]:
|
||||
# 循环处理后置请求参数
|
||||
value = jsonpath.jsonpath(caseres, tddata["path"])[0]
|
||||
if tddata["datatype"] == "param":
|
||||
# 后置参数为param类型时
|
||||
middata = valueHandle(tdcase["data"]["param"])
|
||||
# 为了方便处理 转化为字典 然后更换值之后再转化为param字符串
|
||||
middata[tddata['dataname']] = value
|
||||
tdcase["data"]["param"] = strHandleUrl(middata)
|
||||
elif tddata["datatype"] == "json":
|
||||
# 后置参数为json类型时
|
||||
if not isinstance(tdcase["data"]["param"],dict):
|
||||
raise TypeError("['data']['param']格式错误")
|
||||
tdcase["data"]["param"][tddata['dataname']] = value
|
||||
elif tddata["datatype"] == "urlparam":
|
||||
# 后置参数为url路径类型时
|
||||
tdcase["data"]["urlparam"] = {
|
||||
tddata['dataname']: value
|
||||
}
|
||||
# 执行后置请求
|
||||
apisend(address=tdcase["address"],
|
||||
method=tdcase["method"], headers=tdcase["headers"],
|
||||
data=tdcase["data"],
|
||||
caches=tdcase["cache"], host=tdcase["host"])
|
||||
logging.info(f"成功执行后置接口:{tdcase['info']}")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"执行后置接口:{tdcase['info']}错误,{e}")
|
||||
raise
|
|
@ -5,7 +5,7 @@ from email.header import Header
|
|||
from email.mime.text import MIMEText
|
||||
|
||||
from config.confManage import mail_manage
|
||||
from util.tools.log import Log
|
||||
from scripts.log import Log
|
||||
|
||||
Log()
|
||||
|
||||
|
|
Loading…
Reference in New Issue