增加xml参数类型请求,增加加密处理装饰器

This commit is contained in:
jing song 2022-05-17 17:13:06 +08:00
parent 6140e7db7c
commit cacdc378c1
13 changed files with 154 additions and 28 deletions

View File

@ -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`:包含两种请求格式
1json格式 `{
1json格式 :
`{
"username": "finsiot","password": "$caches(pwd)$" # 读取缓存值
}`
2param格式 `username=admin&password=123`
}`(同样适用于xml格式 会根据请求头***application/xml或者text/xml*** 将字典转换成xml类型)
2param格式
`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文件内自己编辑加密规则
![img_2.png](pic/img_2.png)
需要加密的接口 YAML数据中encryption的值为true 会根据此在请求用例前增加加密装饰器
![img_1.png](pic/img_1.png)
# 六、操作方法
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
![自动生成吃的测试用例](https://img-blog.csdnimg.cn/39ebf79842e14cf791af0fa88433eab3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6bq76L6j54Or5YS_,size_20,color_FFFFFF,t_70,g_se,x_16)
login.yml

View File

@ -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:

View File

@ -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"})))

View File

@ -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()

View File

@ -43,3 +43,5 @@ charset = UTF-8
[dingding]
webhook = https://oapi.dingtalk.com/robot/send?access_token=2e7987d965c9ce0fd3d7a703d9653f9f12237dd9b5f07a21a2e1cb84cc2bbb7e
secret = SEC8867b2fed306092b1005e4c85ca62de803fb3ae2fb34ee6ab6ea90a595aa57a0
[encryption]
sign = 12345hhhhhh

BIN
pic/img_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
pic/img_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -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

View File

@ -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',
]

View File

@ -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:

View File

@ -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

View File

@ -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"""

66
util/tools/encryption.py Normal file
View File

@ -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()