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

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

|
||||
# 六、操作方法
|
||||
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**开始测试(单用例调试时需要执行**util\tools\readYamlFile.py**将测试数据存入redis)
|
||||
# 六、代码展示
|
||||
# 七、代码展示
|
||||
test_login.py
|
||||

|
||||
login.yml
|
||||
|
|
|
@ -3,6 +3,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import random
|
||||
import xmltodict
|
||||
|
||||
import allure
|
||||
import requests
|
||||
|
@ -101,6 +102,12 @@ class apiSend(object):
|
|||
if data_random:
|
||||
data_random = json.loads(data_random)
|
||||
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:
|
||||
response = requests.post(url=url, data=data_random, headers=header, timeout=timeout)
|
||||
try:
|
||||
|
@ -193,6 +200,12 @@ class apiSend(object):
|
|||
if data_random:
|
||||
data_random = json.loads(data_random)
|
||||
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:
|
||||
response = requests.put(url=url, data=data_random, headers=header, timeout=timeout, files=files)
|
||||
try:
|
||||
|
|
|
@ -83,7 +83,17 @@ def dingding_manage(dingding):
|
|||
pass
|
||||
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__':
|
||||
# print(dir_manage("${pro_dir}$"))
|
||||
# print(db_manage("${user}$"))
|
||||
|
@ -91,6 +101,6 @@ if __name__ == '__main__':
|
|||
# print(db_manage("${database}$"))
|
||||
# print(db_manage("${charset}$"))
|
||||
# print(int(db_manage("${port}$")))
|
||||
print(host_manage("${host}$${host_117}$"))
|
||||
print(encryption_manage("${sign}$"))
|
||||
# print("${{{haha}}}$".format(**{"haha":"123"}))
|
||||
# print(host_manage("${{{haha}}}$".format(**{"haha":"host2"})))
|
||||
|
|
|
@ -49,6 +49,13 @@ class Config(object):
|
|||
self.config.read(self.conf_path, encoding='utf-8')
|
||||
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__':
|
||||
c = Config()
|
||||
|
|
|
@ -43,3 +43,5 @@ charset = UTF-8
|
|||
[dingding]
|
||||
webhook = https://oapi.dingtalk.com/robot/send?access_token=2e7987d965c9ce0fd3d7a703d9653f9f12237dd9b5f07a21a2e1cb84cc2bbb7e
|
||||
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-python-commons==2.9.43
|
||||
allure-pytest==2.9.45
|
||||
allure-python-commons==2.9.45
|
||||
asgiref==3.4.1
|
||||
atomicwrites==1.4.0
|
||||
attrs==21.2.0
|
||||
|
@ -8,11 +8,15 @@ Brotli==1.0.9
|
|||
certifi==2021.5.30
|
||||
cffi==1.15.0
|
||||
chardet==4.0.0
|
||||
charset-normalizer==2.0.12
|
||||
click==8.0.3
|
||||
colorama==0.4.4
|
||||
coverage==5.5
|
||||
cryptography==3.4.8
|
||||
cryptography==36.0.2
|
||||
Deprecated==1.2.13
|
||||
DingtalkChatbot==1.5.3
|
||||
execnet==1.9.0
|
||||
Faker==13.3.2
|
||||
Flask==2.0.2
|
||||
h11==0.12.0
|
||||
h2==4.1.0
|
||||
|
@ -26,7 +30,7 @@ jsonpath==0.82
|
|||
kaitaistruct==0.9
|
||||
ldap3==2.9.1
|
||||
MarkupSafe==2.0.1
|
||||
mitmproxy==7.0.4
|
||||
mitmproxy==8.0.0
|
||||
msgpack==1.0.3
|
||||
packaging==20.9
|
||||
passlib==1.7.4
|
||||
|
@ -38,14 +42,18 @@ pyasn1==0.4.8
|
|||
pycparser==2.21
|
||||
pydivert==2.1.0
|
||||
PyMySQL==1.0.2
|
||||
pyOpenSSL==20.0.1
|
||||
pyOpenSSL==22.0.0
|
||||
pyparsing==2.4.7
|
||||
pyperclip==1.8.2
|
||||
pytest==6.2.4
|
||||
pytest==7.1.1
|
||||
pytest-forked==1.4.0
|
||||
pytest-ordering==0.6
|
||||
pytest-rerunfailures==10.0
|
||||
pytest-xdist==2.5.0
|
||||
python-dateutil==2.8.2
|
||||
PyYAML==5.4.1
|
||||
requests==2.25.1
|
||||
redis==4.1.4
|
||||
requests==2.27.1
|
||||
requests-toolbelt==0.9.1
|
||||
ruamel.yaml==0.17.16
|
||||
ruamel.yaml.clib==0.2.6
|
||||
|
@ -53,11 +61,12 @@ simplejson==3.17.2
|
|||
six==1.16.0
|
||||
sortedcontainers==2.4.0
|
||||
toml==0.10.2
|
||||
tomli==2.0.1
|
||||
tornado==6.1
|
||||
urllib3==1.26.5
|
||||
urwid==2.1.2
|
||||
Werkzeug==2.0.2
|
||||
wrapt==1.13.3
|
||||
wsproto==1.0.0
|
||||
xmltodict==0.13.0
|
||||
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.requestsTearDown import caseTearDown
|
||||
from util.tools.readYamlFile import readRedisData
|
||||
|
||||
from util.tools.encryption import Encryption
|
||||
|
||||
Log()
|
||||
__all__ = [
|
||||
|
@ -28,4 +28,5 @@ __all__ = [
|
|||
'relevance',
|
||||
'iniheaders',
|
||||
'caseTearDown',
|
||||
'Encryption',
|
||||
]
|
|
@ -38,6 +38,7 @@ from util.tools.iniRequests import relevance
|
|||
from util.tools.iniHeaders import iniheaders
|
||||
from util.tools.requestsTearDown import caseTearDown
|
||||
from util.tools.readYamlFile import readRedisData
|
||||
from util.tools.encryption import Encryption
|
||||
|
||||
|
||||
Log()
|
||||
|
@ -53,6 +54,7 @@ __all__ = [
|
|||
'relevance',
|
||||
'iniheaders',
|
||||
'caseTearDown',
|
||||
'Encryption',
|
||||
]""")
|
||||
if "conftest.py" not in os.listdir(casepath):
|
||||
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
|
||||
|
||||
Log()
|
||||
Host = host_manage("${HB}$")
|
||||
hostname = "HB"
|
||||
Host = host_manage(f"${{{hostname}}}$")
|
||||
|
||||
|
||||
class Counter:
|
||||
|
@ -60,6 +61,7 @@ class Counter:
|
|||
# detail["order"] = self.num
|
||||
detail["case"] = [case]
|
||||
detail["file"] = False
|
||||
detail["encryption"] = False
|
||||
|
||||
# case 数据
|
||||
case["info"] = name
|
||||
|
|
|
@ -44,6 +44,9 @@ class Test_{filename}(object):""")
|
|||
@pytest.mark.parametrize('casedata', readRedisData("{item}")["case"],
|
||||
ids=[i["info"] for i in readRedisData("{item}")["case"]])
|
||||
@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":
|
||||
# 判断是否需要token 默认类型是 Authorization
|
||||
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