mirror of https://gitee.com/a529548204/apitest.git
增加xml参数类型请求,增加加密处理装饰器
This commit is contained in:
parent
6140e7db7c
commit
cacdc378c1
31
README.md
31
README.md
|
@ -18,7 +18,8 @@ QQ 529548204
|
||||||
allure
|
allure
|
||||||
|
|
||||||
框架采用python的pytest模块,
|
框架采用python的pytest模块,
|
||||||
搭配requests以及allure测试报告,可以发送钉钉通知邮件通知
|
搭配requests以及allure测试报告,可以发送钉钉通知邮件通知,
|
||||||
|
支持自定义接口加密,util.tools.encryption文件内自己编辑加密规则,
|
||||||
可以根据yaml测试数据自动生成用例,
|
可以根据yaml测试数据自动生成用例,
|
||||||
支持接口关联,
|
支持接口关联,
|
||||||
支持类似jmeter的函数助手,
|
支持类似jmeter的函数助手,
|
||||||
|
@ -116,14 +117,18 @@ QQ 529548204
|
||||||
`file` : 通过case外关键字file判断是否需要上传文件 如果需要则格式为:`{上传文件的参数名:文件路径}`
|
`file` : 通过case外关键字file判断是否需要上传文件 如果需要则格式为:`{上传文件的参数名:文件路径}`
|
||||||
|
|
||||||
`param`:包含两种请求格式
|
`param`:包含两种请求格式
|
||||||
(1)json格式 `{
|
(1)json格式 :
|
||||||
|
`{
|
||||||
"username": "finsiot","password": "$caches(pwd)$" # 读取缓存值
|
"username": "finsiot","password": "$caches(pwd)$" # 读取缓存值
|
||||||
}`
|
}`(同样适用于xml格式 会根据请求头***application/xml或者text/xml*** 将字典转换成xml类型)
|
||||||
(2)param格式 `username=admin&password=123`
|
(2)param格式
|
||||||
|
`username=admin&password=123`
|
||||||
|
|
||||||
`urlparam`:`{
|
`urlparam`为路径参数:`{
|
||||||
id: 123
|
id: 123
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
会根据字典转换成
|
||||||
路径参数 **address**中 `v1/api/$url(id)$/`中会根据id替换为123
|
路径参数 **address**中 `v1/api/$url(id)$/`中会根据id替换为123
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,7 +191,7 @@ QQ 529548204
|
||||||
time: 2 # 响应时间断言
|
time: 2 # 响应时间断言
|
||||||
code: 200
|
code: 200
|
||||||
```
|
```
|
||||||
### 1.4 生成随机数据介绍
|
### 1.5 生成随机数据介绍
|
||||||
部分数据采用faker库生成
|
部分数据采用faker库生成
|
||||||
```python
|
```python
|
||||||
int_num = "$RandomPosInt(1,333)$" # 267
|
int_num = "$RandomPosInt(1,333)$" # 267
|
||||||
|
@ -204,7 +209,7 @@ QQ 529548204
|
||||||
name = "$faker(name)$" # 许云
|
name = "$faker(name)$" # 许云
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.4 后置请求处理
|
### 1.6 后置请求处理
|
||||||
使用场景: 新增数据A之后一系列用例执行完需要后置来还原数据形成业务闭环.
|
使用场景: 新增数据A之后一系列用例执行完需要后置来还原数据形成业务闭环.
|
||||||
datatype 有3种类型 param json 和urlparam
|
datatype 有3种类型 param json 和urlparam
|
||||||
dataname为后置关联的参数中参数名称
|
dataname为后置关联的参数中参数名称
|
||||||
|
@ -415,14 +420,20 @@ http=127.0.0.1:4444;https=127.0.0.1:4444;ftp=127.0.0.1:4444
|
||||||
HTTPS:
|
HTTPS:
|
||||||
http://mitm.it/ 开启代理后下载证书安装
|
http://mitm.it/ 开启代理后下载证书安装
|
||||||
执行脚本 recording.py 每个请求将会在 ./test_suite/recording 文件夹中建立文件 每次执行录制时会覆盖原文件
|
执行脚本 recording.py 每个请求将会在 ./test_suite/recording 文件夹中建立文件 每次执行录制时会覆盖原文件
|
||||||
|
# 五、接口加密(测试版)
|
||||||
# 五、操作方法
|
加签加密法:
|
||||||
|
在config中`encryption`增加sign的值 为签
|
||||||
|
需要在util.tools.encryption文件内自己编辑加密规则
|
||||||
|

|
||||||
|
需要加密的接口 YAML数据中encryption的值为true 会根据此在请求用例前增加加密装饰器
|
||||||
|

|
||||||
|
# 六、操作方法
|
||||||
1. 新建config/config.ini文件 格式如上例子
|
1. 新建config/config.ini文件 格式如上例子
|
||||||
2. 执行**util/scripts/newProject.py** 根据testname生成测试项目基础目录
|
2. 执行**util/scripts/newProject.py** 根据testname生成测试项目基础目录
|
||||||
3. 在生成的**test_suite/datas/*testname*** 文件夹下增加yaml测试用例
|
3. 在生成的**test_suite/datas/*testname*** 文件夹下增加yaml测试用例
|
||||||
4. 执行**util/scripts/writeCase.py**生成测试脚本 关于token 需要根据自己项目情况修改yaml文件中token关键字 如果不需要token值为false 需要token则改为需要的类型
|
4. 执行**util/scripts/writeCase.py**生成测试脚本 关于token 需要根据自己项目情况修改yaml文件中token关键字 如果不需要token值为false 需要token则改为需要的类型
|
||||||
5. 执行**setupMain.py**开始测试(单用例调试时需要执行**util\tools\readYamlFile.py**将测试数据存入redis)
|
5. 执行**setupMain.py**开始测试(单用例调试时需要执行**util\tools\readYamlFile.py**将测试数据存入redis)
|
||||||
# 六、代码展示
|
# 七、代码展示
|
||||||
test_login.py
|
test_login.py
|
||||||

|

|
||||||
login.yml
|
login.yml
|
||||||
|
|
|
@ -3,6 +3,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
import allure
|
import allure
|
||||||
import requests
|
import requests
|
||||||
|
@ -101,6 +102,12 @@ class apiSend(object):
|
||||||
if data_random:
|
if data_random:
|
||||||
data_random = json.loads(data_random)
|
data_random = json.loads(data_random)
|
||||||
response = requests.post(url=url, json=data_random, headers=header, timeout=timeout)
|
response = requests.post(url=url, json=data_random, headers=header, timeout=timeout)
|
||||||
|
elif 'application/xml' in str(header.values()) or 'text/xml' in str(header.values()):
|
||||||
|
if data_random:
|
||||||
|
data_random = json.loads(data_random)
|
||||||
|
xmldata = xmltodict.unparse({'xml': data_random})
|
||||||
|
# dict转成xml
|
||||||
|
response = requests.post(url=url, data=xmldata, headers=header, timeout=timeout)
|
||||||
else:
|
else:
|
||||||
response = requests.post(url=url, data=data_random, headers=header, timeout=timeout)
|
response = requests.post(url=url, data=data_random, headers=header, timeout=timeout)
|
||||||
try:
|
try:
|
||||||
|
@ -193,6 +200,12 @@ class apiSend(object):
|
||||||
if data_random:
|
if data_random:
|
||||||
data_random = json.loads(data_random)
|
data_random = json.loads(data_random)
|
||||||
response = requests.put(url=url, json=data_random, headers=header, timeout=timeout, files=files)
|
response = requests.put(url=url, json=data_random, headers=header, timeout=timeout, files=files)
|
||||||
|
elif 'application/xml' in str(header.values()) or 'text/xml' in str(header.values()):
|
||||||
|
if data_random:
|
||||||
|
data_random = json.loads(data_random)
|
||||||
|
xmldata = xmltodict.unparse({'xml': data_random})
|
||||||
|
# dict转成xml
|
||||||
|
response = requests.post(url=url, data=xmldata, headers=header, timeout=timeout)
|
||||||
else:
|
else:
|
||||||
response = requests.put(url=url, data=data_random, headers=header, timeout=timeout, files=files)
|
response = requests.put(url=url, data=data_random, headers=header, timeout=timeout, files=files)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -83,7 +83,17 @@ def dingding_manage(dingding):
|
||||||
pass
|
pass
|
||||||
return dingding
|
return dingding
|
||||||
|
|
||||||
|
def encryption_manage(encryption):
|
||||||
|
try:
|
||||||
|
relevance_list = re.findall(r"\${(.*?)}\$", encryption)
|
||||||
|
for n in relevance_list:
|
||||||
|
pattern = re.compile(r'\${' + n + r'}\$')
|
||||||
|
dir_cf = Config()
|
||||||
|
dir_relevance = dir_cf.read_encryption()
|
||||||
|
encryption = re.sub(pattern, dir_relevance[n], encryption, count=1)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
return encryption
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# print(dir_manage("${pro_dir}$"))
|
# print(dir_manage("${pro_dir}$"))
|
||||||
# print(db_manage("${user}$"))
|
# print(db_manage("${user}$"))
|
||||||
|
@ -91,6 +101,6 @@ if __name__ == '__main__':
|
||||||
# print(db_manage("${database}$"))
|
# print(db_manage("${database}$"))
|
||||||
# print(db_manage("${charset}$"))
|
# print(db_manage("${charset}$"))
|
||||||
# print(int(db_manage("${port}$")))
|
# print(int(db_manage("${port}$")))
|
||||||
print(host_manage("${host}$${host_117}$"))
|
print(encryption_manage("${sign}$"))
|
||||||
# print("${{{haha}}}$".format(**{"haha":"123"}))
|
# print("${{{haha}}}$".format(**{"haha":"123"}))
|
||||||
# print(host_manage("${{{haha}}}$".format(**{"haha":"host2"})))
|
# print(host_manage("${{{haha}}}$".format(**{"haha":"host2"})))
|
||||||
|
|
|
@ -49,6 +49,13 @@ class Config(object):
|
||||||
self.config.read(self.conf_path, encoding='utf-8')
|
self.config.read(self.conf_path, encoding='utf-8')
|
||||||
return self.config['dingding']
|
return self.config['dingding']
|
||||||
|
|
||||||
|
def read_encryption(self):
|
||||||
|
"""
|
||||||
|
读取加密配置签名
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.config.read(self.conf_path, encoding='utf-8')
|
||||||
|
return self.config['encryption']
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
c = Config()
|
c = Config()
|
||||||
|
|
|
@ -43,3 +43,5 @@ charset = UTF-8
|
||||||
[dingding]
|
[dingding]
|
||||||
webhook = https://oapi.dingtalk.com/robot/send?access_token=2e7987d965c9ce0fd3d7a703d9653f9f12237dd9b5f07a21a2e1cb84cc2bbb7e
|
webhook = https://oapi.dingtalk.com/robot/send?access_token=2e7987d965c9ce0fd3d7a703d9653f9f12237dd9b5f07a21a2e1cb84cc2bbb7e
|
||||||
secret = SEC8867b2fed306092b1005e4c85ca62de803fb3ae2fb34ee6ab6ea90a595aa57a0
|
secret = SEC8867b2fed306092b1005e4c85ca62de803fb3ae2fb34ee6ab6ea90a595aa57a0
|
||||||
|
[encryption]
|
||||||
|
sign = 12345hhhhhh
|
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
|
@ -1,5 +1,5 @@
|
||||||
allure-pytest==2.9.43
|
allure-pytest==2.9.45
|
||||||
allure-python-commons==2.9.43
|
allure-python-commons==2.9.45
|
||||||
asgiref==3.4.1
|
asgiref==3.4.1
|
||||||
atomicwrites==1.4.0
|
atomicwrites==1.4.0
|
||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
|
@ -8,11 +8,15 @@ Brotli==1.0.9
|
||||||
certifi==2021.5.30
|
certifi==2021.5.30
|
||||||
cffi==1.15.0
|
cffi==1.15.0
|
||||||
chardet==4.0.0
|
chardet==4.0.0
|
||||||
|
charset-normalizer==2.0.12
|
||||||
click==8.0.3
|
click==8.0.3
|
||||||
colorama==0.4.4
|
colorama==0.4.4
|
||||||
coverage==5.5
|
coverage==5.5
|
||||||
cryptography==3.4.8
|
cryptography==36.0.2
|
||||||
|
Deprecated==1.2.13
|
||||||
DingtalkChatbot==1.5.3
|
DingtalkChatbot==1.5.3
|
||||||
|
execnet==1.9.0
|
||||||
|
Faker==13.3.2
|
||||||
Flask==2.0.2
|
Flask==2.0.2
|
||||||
h11==0.12.0
|
h11==0.12.0
|
||||||
h2==4.1.0
|
h2==4.1.0
|
||||||
|
@ -26,7 +30,7 @@ jsonpath==0.82
|
||||||
kaitaistruct==0.9
|
kaitaistruct==0.9
|
||||||
ldap3==2.9.1
|
ldap3==2.9.1
|
||||||
MarkupSafe==2.0.1
|
MarkupSafe==2.0.1
|
||||||
mitmproxy==7.0.4
|
mitmproxy==8.0.0
|
||||||
msgpack==1.0.3
|
msgpack==1.0.3
|
||||||
packaging==20.9
|
packaging==20.9
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
|
@ -38,14 +42,18 @@ pyasn1==0.4.8
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
pydivert==2.1.0
|
pydivert==2.1.0
|
||||||
PyMySQL==1.0.2
|
PyMySQL==1.0.2
|
||||||
pyOpenSSL==20.0.1
|
pyOpenSSL==22.0.0
|
||||||
pyparsing==2.4.7
|
pyparsing==2.4.7
|
||||||
pyperclip==1.8.2
|
pyperclip==1.8.2
|
||||||
pytest==6.2.4
|
pytest==7.1.1
|
||||||
|
pytest-forked==1.4.0
|
||||||
pytest-ordering==0.6
|
pytest-ordering==0.6
|
||||||
pytest-rerunfailures==10.0
|
pytest-rerunfailures==10.0
|
||||||
|
pytest-xdist==2.5.0
|
||||||
|
python-dateutil==2.8.2
|
||||||
PyYAML==5.4.1
|
PyYAML==5.4.1
|
||||||
requests==2.25.1
|
redis==4.1.4
|
||||||
|
requests==2.27.1
|
||||||
requests-toolbelt==0.9.1
|
requests-toolbelt==0.9.1
|
||||||
ruamel.yaml==0.17.16
|
ruamel.yaml==0.17.16
|
||||||
ruamel.yaml.clib==0.2.6
|
ruamel.yaml.clib==0.2.6
|
||||||
|
@ -53,11 +61,12 @@ simplejson==3.17.2
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
sortedcontainers==2.4.0
|
sortedcontainers==2.4.0
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
|
tomli==2.0.1
|
||||||
tornado==6.1
|
tornado==6.1
|
||||||
urllib3==1.26.5
|
urllib3==1.26.5
|
||||||
urwid==2.1.2
|
urwid==2.1.2
|
||||||
Werkzeug==2.0.2
|
Werkzeug==2.0.2
|
||||||
|
wrapt==1.13.3
|
||||||
wsproto==1.0.0
|
wsproto==1.0.0
|
||||||
|
xmltodict==0.13.0
|
||||||
zstandard==0.15.2
|
zstandard==0.15.2
|
||||||
faker==13.3.2
|
|
||||||
pytest-xdist==2.5.0
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from util.tools.iniRequests import relevance
|
||||||
from util.tools.iniHeaders import iniheaders
|
from util.tools.iniHeaders import iniheaders
|
||||||
from util.tools.requestsTearDown import caseTearDown
|
from util.tools.requestsTearDown import caseTearDown
|
||||||
from util.tools.readYamlFile import readRedisData
|
from util.tools.readYamlFile import readRedisData
|
||||||
|
from util.tools.encryption import Encryption
|
||||||
|
|
||||||
Log()
|
Log()
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -28,4 +28,5 @@ __all__ = [
|
||||||
'relevance',
|
'relevance',
|
||||||
'iniheaders',
|
'iniheaders',
|
||||||
'caseTearDown',
|
'caseTearDown',
|
||||||
|
'Encryption',
|
||||||
]
|
]
|
|
@ -38,6 +38,7 @@ from util.tools.iniRequests import relevance
|
||||||
from util.tools.iniHeaders import iniheaders
|
from util.tools.iniHeaders import iniheaders
|
||||||
from util.tools.requestsTearDown import caseTearDown
|
from util.tools.requestsTearDown import caseTearDown
|
||||||
from util.tools.readYamlFile import readRedisData
|
from util.tools.readYamlFile import readRedisData
|
||||||
|
from util.tools.encryption import Encryption
|
||||||
|
|
||||||
|
|
||||||
Log()
|
Log()
|
||||||
|
@ -53,6 +54,7 @@ __all__ = [
|
||||||
'relevance',
|
'relevance',
|
||||||
'iniheaders',
|
'iniheaders',
|
||||||
'caseTearDown',
|
'caseTearDown',
|
||||||
|
'Encryption',
|
||||||
]""")
|
]""")
|
||||||
if "conftest.py" not in os.listdir(casepath):
|
if "conftest.py" not in os.listdir(casepath):
|
||||||
with open(casepath + "/" + r"{}".format("conftest.py"), 'w', encoding='utf-8') as f:
|
with open(casepath + "/" + r"{}".format("conftest.py"), 'w', encoding='utf-8') as f:
|
||||||
|
|
|
@ -16,7 +16,8 @@ from util.tools.log import Log
|
||||||
from util.tools.mkDir import mk_dir
|
from util.tools.mkDir import mk_dir
|
||||||
|
|
||||||
Log()
|
Log()
|
||||||
Host = host_manage("${HB}$")
|
hostname = "HB"
|
||||||
|
Host = host_manage(f"${{{hostname}}}$")
|
||||||
|
|
||||||
|
|
||||||
class Counter:
|
class Counter:
|
||||||
|
@ -60,6 +61,7 @@ class Counter:
|
||||||
# detail["order"] = self.num
|
# detail["order"] = self.num
|
||||||
detail["case"] = [case]
|
detail["case"] = [case]
|
||||||
detail["file"] = False
|
detail["file"] = False
|
||||||
|
detail["encryption"] = False
|
||||||
|
|
||||||
# case 数据
|
# case 数据
|
||||||
case["info"] = name
|
case["info"] = name
|
||||||
|
|
|
@ -44,6 +44,9 @@ class Test_{filename}(object):""")
|
||||||
@pytest.mark.parametrize('casedata', readRedisData("{item}")["case"],
|
@pytest.mark.parametrize('casedata', readRedisData("{item}")["case"],
|
||||||
ids=[i["info"] for i in readRedisData("{item}")["case"]])
|
ids=[i["info"] for i in readRedisData("{item}")["case"]])
|
||||||
@pytest.mark.flaky(reruns=1, reruns_delay=1)""")
|
@pytest.mark.flaky(reruns=1, reruns_delay=1)""")
|
||||||
|
if filedata[item]["encryption"]:
|
||||||
|
f.write(f"""
|
||||||
|
@Encryption()""")
|
||||||
if filedata[item]["token"] and filedata[item]["token"] == "Cookies":
|
if filedata[item]["token"] and filedata[item]["token"] == "Cookies":
|
||||||
# 判断是否需要token 默认类型是 Authorization
|
# 判断是否需要token 默认类型是 Authorization
|
||||||
f.write(f"""
|
f.write(f"""
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# coding:utf-8
|
||||||
|
"""
|
||||||
|
@author: jing
|
||||||
|
@contact: 529548204@qq.com
|
||||||
|
@file: encryption.py
|
||||||
|
@time: 2022/5/17 14:22
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from config.confManage import encryption_manage
|
||||||
|
|
||||||
|
|
||||||
|
class Encryption(object):
|
||||||
|
|
||||||
|
def __init__(self, api_key=encryption_manage("${sign}$")):
|
||||||
|
self.api_key = api_key
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
|
def __call__(self, func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapped_function(*args, **kwargs):
|
||||||
|
# 打开logfile并写入
|
||||||
|
# print(kwargs["casedata"]["data"])
|
||||||
|
self.data = kwargs["casedata"]["data"]
|
||||||
|
kwargs["casedata"]["data"]["param"] = self.custom_encryption()
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapped_function
|
||||||
|
|
||||||
|
def custom_encryption(self):
|
||||||
|
"""
|
||||||
|
自定义加密规则 (规则需要自己写以下为例子)
|
||||||
|
返回结果是加密后全部参数 可以是"a=1&b=2"也可以是{"a":1,"b":2}
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if "sign" in self.data["param"]:
|
||||||
|
# 弹出sign
|
||||||
|
self.data["param"].pop('sign')
|
||||||
|
dataList = []
|
||||||
|
for key in sorted(self.data["param"]):
|
||||||
|
if self.data["param"][key]:
|
||||||
|
dataList.append("%s=%s" % (key, self.data["param"][key]))
|
||||||
|
lastdata = "&".join(dataList)
|
||||||
|
data = lastdata + "&key=" + self.api_key.strip()
|
||||||
|
md = hashlib.md5()
|
||||||
|
md.update(data.encode("utf-8"))
|
||||||
|
signature = md.hexdigest().upper()
|
||||||
|
self.data["param"]['sign'] = signature
|
||||||
|
dictxml = self.data["param"]
|
||||||
|
# dict转成xml
|
||||||
|
return dictxml
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
api_keys = "8ViypSUsKSbK26g7HDsKtZSCTUsTtEvB"
|
||||||
|
da = {'mch_id': 160467, 'nonce_str': 'eh0k52jvvvs51dbxr2pd', 'out_trade_no': 10002112123231, 'sign': '{{sign}}'}
|
||||||
|
|
||||||
|
# s = logit(api_keys,da)
|
||||||
|
# s.send_post()
|
||||||
|
|
||||||
|
# s.data_processing()
|
||||||
|
# s.md5()
|
||||||
|
# print(s.rewrite_xml())
|
||||||
|
# s.rewrite_xml()
|
Loading…
Reference in New Issue