From bd6036b2f0ae920a0eb2c525bc17a7d71fe2bc0d Mon Sep 17 00:00:00 2001 From: jing song <529548204@qq.com> Date: Wed, 11 May 2022 16:23:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8E=E7=BD=AE=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=8A=9F=E8=83=BD=E8=AF=A6=E6=83=85=E8=A7=81=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 71 ++++++++++++++++++++++----- pic/img.png | Bin 0 -> 9069 bytes test_suite/datas/demo/biaodan.yml | 3 +- test_suite/datas/demo/login.yml | 1 + test_suite/testcase/demo/__init__.py | 4 ++ util/scripts/newProject.py | 4 ++ util/scripts/recording.py | 1 + util/scripts/writeCase.py | 22 +++++++-- util/tools/caches.py | 28 ++--------- util/tools/datasTypeChange.py | 52 ++++++++++++++++++++ util/tools/iniHeaders.py | 17 +++++++ util/tools/iniRequests.py | 56 +++++++++++++-------- util/tools/readYamlFile.py | 4 +- util/tools/requestsTearDown.py | 71 +++++++++++++++++++++++++++ util/tools/sendEmail.py | 2 +- 15 files changed, 273 insertions(+), 63 deletions(-) create mode 100644 pic/img.png create mode 100644 util/tools/datasTypeChange.py create mode 100644 util/tools/iniHeaders.py create mode 100644 util/tools/requestsTearDown.py diff --git a/README.md b/README.md index fdb3c2b..5778d3a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,10 @@ QQ 529548204 下面示例为 关联接口为tradeAdd 与 tradeAdd2两条yaml数据中case的第一条和第二条 分别去这两个接口返回结果的 id 并命名为tradeId 、tradeId2 内存中会保存成字典格式 -`{"tradeId":"10","tradeId2":"11"}` +`{"tradeId":"10","tradeId2":"11"}` +读取方法:`$relevance(tradeId2)$` + +![img.png](pic/img.png) ```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 ![自动生成吃的测试用例](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 diff --git a/pic/img.png b/pic/img.png new file mode 100644 index 0000000000000000000000000000000000000000..53de89eeaec1f65c41fac20986cbc9a58aaa9ae5 GIT binary patch literal 9069 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 literal 0 HcmV?d00001 diff --git a/test_suite/datas/demo/biaodan.yml b/test_suite/datas/demo/biaodan.yml index e6a4b49..2cf9d7f 100644 --- a/test_suite/datas/demo/biaodan.yml +++ b/test_suite/datas/demo/biaodan.yml @@ -21,4 +21,5 @@ shoucang: info: method: POST cache: - relevance: \ No newline at end of file + relevance: + teardown: \ No newline at end of file diff --git a/test_suite/datas/demo/login.yml b/test_suite/datas/demo/login.yml index 7570d51..d64bbc2 100644 --- a/test_suite/datas/demo/login.yml +++ b/test_suite/datas/demo/login.yml @@ -25,3 +25,4 @@ login: path: # body "id=2&path=haha" תֵ Ȼpathʹjsonpathȡֵ name: 'cookie' relevance: + teardown: diff --git a/test_suite/testcase/demo/__init__.py b/test_suite/testcase/demo/__init__.py index 1189e7e..9b7e5c9 100644 --- a/test_suite/testcase/demo/__init__.py +++ b/test_suite/testcase/demo/__init__.py @@ -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', ] \ No newline at end of file diff --git a/util/scripts/newProject.py b/util/scripts/newProject.py index 9dce4c2..c6bdbf2 100644 --- a/util/scripts/newProject.py +++ b/util/scripts/newProject.py @@ -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: diff --git a/util/scripts/recording.py b/util/scripts/recording.py index f503209..2439034 100644 --- a/util/scripts/recording.py +++ b/util/scripts/recording.py @@ -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) diff --git a/util/scripts/writeCase.py b/util/scripts/writeCase.py index e158b96..a35283d 100644 --- a/util/scripts/writeCase.py +++ b/util/scripts/writeCase.py @@ -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__': diff --git a/util/tools/caches.py b/util/tools/caches.py index ef14936..3a61f9e 100644 --- a/util/tools/caches.py +++ b/util/tools/caches.py @@ -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']) diff --git a/util/tools/datasTypeChange.py b/util/tools/datasTypeChange.py new file mode 100644 index 0000000..403657f --- /dev/null +++ b/util/tools/datasTypeChange.py @@ -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)) \ No newline at end of file diff --git a/util/tools/iniHeaders.py b/util/tools/iniHeaders.py new file mode 100644 index 0000000..aaadec2 --- /dev/null +++ b/util/tools/iniHeaders.py @@ -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 diff --git a/util/tools/iniRequests.py b/util/tools/iniRequests.py index 13b2749..2e78288 100644 --- a/util/tools/iniRequests.py +++ b/util/tools/iniRequests.py @@ -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}, diff --git a/util/tools/readYamlFile.py b/util/tools/readYamlFile.py index e2316b0..27ec0af 100644 --- a/util/tools/readYamlFile.py +++ b/util/tools/readYamlFile.py @@ -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) diff --git a/util/tools/requestsTearDown.py b/util/tools/requestsTearDown.py new file mode 100644 index 0000000..58a624f --- /dev/null +++ b/util/tools/requestsTearDown.py @@ -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 diff --git a/util/tools/sendEmail.py b/util/tools/sendEmail.py index d790298..2a29171 100644 --- a/util/tools/sendEmail.py +++ b/util/tools/sendEmail.py @@ -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()