├── .project ├── .pydevproject ├── Common ├── CheckJson.py ├── CheckResult.py ├── CustomFail.py ├── FunctionReplace.py ├── GetRelevance.py ├── HostManage.py ├── IniCase.py ├── IniRelevance.py ├── MkDir.py ├── ParamManage.py ├── ReadParam.py ├── TestAndCheck.py ├── __init__.py ├── confighttp.py ├── expectedManage.py ├── init.py └── requestSend.py ├── Linux.sh ├── README.md ├── RandomData ├── ChoiceData.py ├── GetTime.py ├── Md5Data.py ├── RandomFloat.py ├── RandomInt.py ├── RandomString.py └── __init__.py ├── TestCase ├── TestAddProject │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── logs │ │ ├── 2018-11-13.log │ │ ├── 2018-11-13_error.log │ │ ├── 2018-11-15.log │ │ └── 2018-11-15_error.log │ ├── param.json │ ├── relevance.ini │ └── test_AddProject.py ├── TestLogin │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── logs │ │ ├── 2018-11-13.log │ │ ├── 2018-11-13_error.log │ │ ├── 2018-11-15.log │ │ └── 2018-11-15_error.log │ ├── param.json │ ├── relevance.ini │ └── test_login.py ├── TestSmallNumberBindUnbind │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_smalNumber.py └── __init__.py ├── TestScene ├── Test_Refund │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_Refundt.py ├── __init__.py ├── test_IdBinding │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_IdBinding.py ├── test_ModifySend │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_ModifySend.py ├── test_OrderUnbinding │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_OrderUnbinding.py ├── test_ReturnGoods │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_ReturnGoods.py ├── test_ZhongTaiCancel │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_ZhongTaiCancel.py ├── test_addProject │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── logs │ │ ├── 2018-11-13.log │ │ └── 2018-11-13_error.log │ ├── param.json │ ├── relevance.ini │ └── test_AddProjectscene.py └── test_cancel │ ├── __init__.py │ ├── case.yaml │ ├── expected.json │ ├── param.json │ ├── relevance.ini │ └── test_cancel.py ├── config ├── ConfRelevance.py ├── ConfigLogs.py ├── __init__.py ├── configHost.py └── host.ini ├── conftest.py ├── data └── cover.jpg ├── logs ├── 2018-11-12.log ├── 2018-11-12_error.log ├── 2018-11-13.log ├── 2018-11-13_error.log ├── 2018-11-15.log └── 2018-11-15_error.log ├── main.py ├── requirements.txt └── window运行.bat /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | test_interface 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /${PROJECT_DIR_NAME} 5 | 6 | python 2.7 7 | py2.7.5 8 | 9 | -------------------------------------------------------------------------------- /Common/CheckJson.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | from main import failureException 13 | 14 | result = 'success' # 初始化结果结果为success 15 | 16 | 17 | def check_json(src_data, dst_data, success): 18 | """ 19 | 校验的json 20 | :param src_data: 校验内容 21 | :param dst_data: 接口返回的数据(被校验的内容) 22 | :param success: 全局测试结果 23 | :return: 24 | """ 25 | global result 26 | if isinstance(src_data, dict): 27 | # 若为dict格式 28 | for key in src_data: 29 | if key not in dst_data: 30 | success["result"] = False 31 | raise failureException("JSON格式校验,关键字 %s 不在返回结果 %s" % (key, dst_data)) 32 | else: 33 | this_key = key 34 | # 递归 35 | if isinstance(src_data[this_key], dict) and isinstance(dst_data[this_key], dict): 36 | check_json(src_data[this_key], dst_data[this_key], success) 37 | elif isinstance(type(src_data[this_key]), type(dst_data[this_key])): 38 | success["result"] = False 39 | raise failureException("JSON格式校验,关键字 %s 与 %s 类型不符" % (src_data[this_key], dst_data[this_key])) 40 | else: 41 | pass 42 | else: 43 | success["result"] = False 44 | raise failureException("JSON校验内容非dict格式") 45 | 46 | 47 | if __name__ == "__main__": 48 | src = {"name": "李四"} 49 | dst = {"name": "张三", "password": "123456"} 50 | dis_src = {"age": 12} 51 | _success = dict() 52 | _success["result"] = True 53 | check_json(src, dst, _success) 54 | print(_success["result"]) 55 | check_json(dis_src, dst, _success) 56 | print(_success["result"]) 57 | -------------------------------------------------------------------------------- /Common/CheckResult.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import operator 13 | import re 14 | 15 | import allure 16 | 17 | from Common import CheckJson, expectedManage, CustomFail 18 | from main import failureException 19 | 20 | 21 | def check(test_name, case_data, code, data, relevance, _path, success): 22 | """ 23 | 校验测试结果 24 | :param test_name: 测试用例 25 | :param case_data: 测试用例 26 | :param code: HTTP状态 27 | :param data: 返回的接口json数据 28 | :param relevance: 关联值对象 29 | :param _path: case路径 30 | :param success: 全局测试结果 31 | :return: 32 | """ 33 | # 不校验 34 | if case_data["check_type"] == 'no_check': 35 | with allure.step("不校验结果"): 36 | pass 37 | 38 | # 校验json格式 39 | elif case_data["check_type"] == 'json': 40 | expected_request = case_data["expected_request"] 41 | # 判断预期结果格式,如果是字符串,则打开文件路径,提取保存在文件中的期望结果 42 | if isinstance(case_data["expected_request"], str): 43 | expected_request = expectedManage.read_json(test_name, expected_request, relevance, _path, success) 44 | with allure.step("JSON格式校验"): 45 | allure.attach("期望code", str(case_data["expected_code"])) 46 | allure.attach('期望data', str(expected_request)) 47 | allure.attach("实际code", str(code)) 48 | allure.attach('实际data', str(data)) 49 | if int(code) == case_data["expected_code"]: 50 | if not data: 51 | data = "{}" 52 | # json校验 53 | CheckJson.check_json(expected_request, data, success) 54 | else: 55 | success["result"] = False 56 | if case_data.get("CustomFail"): 57 | info = CustomFail.custom_manage(case_data.get("CustomFail"), relevance) 58 | raise failureException(str(info)+"\nhttp状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 59 | else: 60 | raise failureException("http状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 61 | 62 | # 只校验HTTP状态 63 | elif case_data["check_type"] == 'only_check_status': 64 | with allure.step("校验HTTP状态"): 65 | allure.attach("期望code", str(case_data["expected_code"])) 66 | allure.attach("实际code", str(code)) 67 | if int(code) == case_data["expected_code"]: 68 | pass 69 | else: 70 | success["result"] = False 71 | if case_data.get("CustomFail"): 72 | info = CustomFail.custom_manage(case_data.get("CustomFail"), relevance) 73 | raise failureException(str(info)+"\nhttp状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 74 | else: 75 | raise failureException("http状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 76 | 77 | # 完全校验 78 | elif case_data["check_type"] == 'entirely_check': 79 | expected_request = case_data["expected_request"] 80 | # 判断预期结果格式,如果是字符串,则打开文件路径,提取保存在文件中的期望结果 81 | if isinstance(case_data["expected_request"], str): 82 | expected_request = expectedManage.read_json(test_name, expected_request, relevance, _path, success) 83 | with allure.step("完全校验"): 84 | allure.attach("期望code", str(case_data["expected_code"])) 85 | allure.attach('期望data', str(expected_request)) 86 | allure.attach("实际code", str(code)) 87 | allure.attach('实际data', str(data)) 88 | if int(code) == case_data["expected_code"]: 89 | result = operator.eq(expected_request, data) 90 | if result: 91 | pass 92 | else: 93 | success["result"] = False 94 | if case_data.get("CustomFail"): 95 | info = CustomFail.custom_manage(case_data.get("CustomFail"), relevance) 96 | raise failureException(str(info)+"\n完全校验失败! %s ! = %s" % (expected_request, data)) 97 | else: 98 | raise failureException("完全校验失败! %s ! = %s" % (expected_request, data)) 99 | else: 100 | success["result"] = False 101 | raise failureException("http状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 102 | 103 | # 正则校验 104 | elif case_data["check_type"] == 'Regular_check': 105 | if int(code) == case_data["expected_code"]: 106 | try: 107 | result = "" # 初始化校验内容 108 | if isinstance(case_data["expected_request"], list): 109 | # 多个正则表达式校验,遍历校验 110 | for i in case_data["expected_request"]: 111 | result = re.findall(i.replace("\"", "\'"), str(data)) 112 | allure.attach('校验完成结果\n', str(result)) 113 | else: 114 | # 单个正则表达式 115 | result = re.findall(case_data["expected_request"].replace("\"", "\'"), str(data)) 116 | with allure.step("正则校验"): 117 | allure.attach("期望code", str(case_data["expected_code"])) 118 | allure.attach('正则表达式', str(case_data["expected_request"]).replace("\'", "\"")) 119 | allure.attach("实际code", str(code)) 120 | allure.attach('实际data', str(data)) 121 | allure.attach(case_data["expected_request"].replace("\"", "\'")+'校验完成结果', 122 | str(result).replace("\'", "\"")) 123 | # 未匹配到校验内容 124 | if not result: 125 | success["result"] = False 126 | if case_data.get("CustomFail"): 127 | info = CustomFail.custom_manage(case_data.get("CustomFail"), relevance) 128 | raise failureException(str(info) + "\n正则未校验到内容! %s" % case_data["expected_request"]) 129 | else: 130 | raise failureException("正则未校验到内容! %s" % case_data["expected_request"]) 131 | # 正则表达式为空时 132 | except KeyError: 133 | success["result"] = False 134 | raise failureException("正则校验执行失败! %s\n正则表达式为空时" % case_data["expected_request"]) 135 | else: 136 | success["result"] = False 137 | raise failureException("http状态码错误!\n %s != %s" % (code, case_data["expected_code"])) 138 | 139 | # 数据库校验 140 | elif case_data["check_type"] == "datebase_check": 141 | pass 142 | else: 143 | success["result"] = False 144 | raise failureException("无该校验方式%s" % case_data["check_type"]) 145 | 146 | 147 | if __name__ == "__main__": 148 | _test_name = '登陆1' 149 | _case_data = {'check_type': 'no_check', 'datebase': None, 'expected_code': None, 'expected_request': None, 'CustomFail': None} 150 | _code = 200 151 | _data = {'code': '999999', 'msg': '成功', 'data': {'first_name': 'Tom', 'last_name': '', 'phone': '123456789', 'email': '123@qq.com', 'key': 'e1dfbfd759ad46bcdc44df989ade3f1c190cc936', 'date_joined': '2018-06-28 02:54:00', 'userphoto': '/file/userphoto.jpg'}} 152 | _relevance = {'name': '123', 'type': 'Web', 'version': '1.2.0', 'description': '这是一个描述', 'first_name': 'Tom'} 153 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_01_login' 154 | _success = {'result': True} 155 | check(_test_name, _case_data, _code, _data, _relevance, path, _success) 156 | -------------------------------------------------------------------------------- /Common/CustomFail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 20:57 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: CustomFail.py 10 | 11 | # @Software: PyCharm 12 | import re 13 | 14 | 15 | def custom_manage(custom_fail, relevance): 16 | """ 17 | 自定义关联配置 18 | :param custom_fail: 自定义错误说明 19 | :param relevance: 关联对象 20 | :return: 21 | """ 22 | try: 23 | relevance_list = re.findall("\${(.*?)}\$", custom_fail) # 查找字符串中所有$key$ 作为关联对象 24 | for n in relevance_list: # 遍历关联key 25 | pattern = re.compile('\${' + n + '}\$') # 初始化正则匹配 26 | custom_fail = re.sub(pattern, relevance[n], custom_fail, count=1) # 关联值1次 27 | except TypeError: 28 | pass 29 | return custom_fail 30 | 31 | 32 | if __name__ == "__main__": 33 | _custom_fail = "这是一段${test}$用的数据" 34 | _relevance = {"test": "测试"} 35 | print(custom_manage(_custom_fail, _relevance)) 36 | 37 | -------------------------------------------------------------------------------- /Common/FunctionReplace.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 17:07 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: FunctionReplace.py 10 | 11 | # @Software: PyCharm 12 | import re 13 | 14 | from RandomData import RandomString, RandomInt, GetTime, ChoiceData, RandomFloat, Md5Data 15 | 16 | 17 | def function_replace(value): 18 | """ 19 | 调用定义方法替换字符串 20 | :param value: 21 | :return: 22 | """ 23 | int_list = re.findall('\$RandomInt\(([0-9]*,[0-9]*?)\)\$', value) 24 | string_list = re.findall('\$RandomString\(([0-9]*?)\)\$', value) 25 | float_list = re.findall("\$RandomFloat\(([0-9]*,[0-9]*,[0-9]*)\)\$", value) 26 | time_list = re.findall("\$GetTime\(time_type=(.*?),layout=(.*?),unit=([0-9],[0-9],[0-9],[0-9],[0-9])\)\$", value) 27 | choice_list = re.findall("\$Choice\(((?!\$Choice\().*?)\)list\$", value) 28 | if len(int_list): 29 | # 获取整型数据替换 30 | for i in int_list: 31 | pattern = re.compile('\$RandomInt\(' + i + '\)\$') # 初始化正则匹配 32 | k = str(RandomInt.random_int(i)) 33 | value = re.sub(pattern, k, value, count=1) 34 | value = function_replace(value) 35 | elif len(string_list): 36 | # 获取字符串替换 37 | for j in string_list: 38 | pattern = re.compile('\$RandomString\(' + j + '\)\$') # 初始化正则匹配 39 | k = RandomString.random_string(j) 40 | value = re.sub(pattern, k, value, count=1) 41 | value = function_replace(value) 42 | elif len(float_list): 43 | # 获取浮点数 44 | for n in float_list: 45 | if len(n.split(",")) == 3: 46 | pattern = re.compile('\$RandomFloat\(' + n + '\)\$') # 初始化正则匹配 47 | k = str(RandomFloat.random_float(n)) 48 | value = re.sub(pattern, k, value, count=1) 49 | value = function_replace(value) 50 | elif len(time_list): 51 | # 获取时间替换 52 | for n in time_list: 53 | if len(n[0]) and len(n[1]): 54 | pattern = re.compile('\$GetTime\(time_type='+n[0]+',layout='+n[1]+',unit='+n[2]+'\)\$') # 初始化正则匹配 55 | k = str(GetTime.get_time(n[0], n[1], n[2])) 56 | value = re.sub(pattern, k, value, count=1) 57 | value = function_replace(value) 58 | elif len(choice_list): 59 | # 调用choice方法 60 | for n in choice_list: 61 | pattern = re.compile('\$Choice\(' + n + '\)list\$') # 初始化正则匹配 62 | k = str(ChoiceData.choice_data(n)) 63 | value = re.sub(pattern, k, value, count=1) 64 | value = function_replace(value) 65 | else: 66 | # md5加密 67 | value = Md5Data.re_md5(value) 68 | return value 69 | 70 | 71 | if __name__ == '__main__': 72 | int_num = '$RandomInt($RandomInt($RandomInt(1,11)$,$RandomInt(2,12)$)$,$RandomInt($RandomInt(3,13)$,$RandomInt(4,14)$)$)$' \ 73 | '$RandomInt($RandomInt($RandomInt(5,15)$,$RandomInt(6,16)$)$,$RandomInt($RandomInt(7,17)$,$RandomInt(8,18)$)$)$' 74 | float_num = '$RandomFloat($RandomInt(1,11)$,$RandomInt(1,11)$,$RandomInt(1,11)$)$' 75 | string_num = '$RandomString($RandomInt($RandomInt(1,11)$,$RandomInt(2,12)$)$)$' 76 | time_num = '$GetTime(time_type=now,layout=13timestamp,unit=5,5,5,5,5)$' 77 | choice_num = '$RandomInt($Choice($Choice(' + int_num + ',' + int_num + ',' + int_num + ',' + int_num + \ 78 | ')list$)list$,$Choice($Choice(' + int_num + ',' + int_num + ',' + int_num + ',' + int_num + ')list$)list$)$' 79 | print(function_replace(int_num)) 80 | print(function_replace(string_num)) 81 | print(function_replace(time_num)) 82 | print(function_replace(float_num)) 83 | print(function_replace(choice_num)) 84 | -------------------------------------------------------------------------------- /Common/GetRelevance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import logging 13 | 14 | from Common.ParamManage import get_value 15 | 16 | 17 | def get_relevance(data, relevance_list, relevance): 18 | """ 19 | 从返回结果中获取关联键的值 20 | :param data: 返回结果 21 | :param relevance_list: 关联键列表 22 | :param relevance: 关联对象 23 | :return: 24 | """ 25 | # 关联键是否时list 26 | if not relevance_list: 27 | return relevance 28 | logging.debug("从返回结果中根据关联键%s提取值" % relevance_list) 29 | if isinstance(relevance_list, list): 30 | # 遍历关联键 31 | for j in relevance_list: 32 | # 从结果中提取关联键的值 33 | relevance_value = get_value(data, j) 34 | if relevance_value: 35 | # 考虑到一个关联键,多个值 36 | if j in relevance: 37 | if isinstance(relevance[j], list): 38 | a = relevance[j] 39 | a.append(relevance_value) 40 | relevance[j] = a 41 | else: 42 | a = relevance[j] 43 | b = list() 44 | b.append(a) 45 | b.append(relevance_value) 46 | relevance[j] = b 47 | else: 48 | relevance[j] = relevance_value 49 | else: 50 | return False 51 | else: 52 | relevance_value = get_value(data, relevance_list) 53 | if relevance_value: 54 | # 考虑到一个关联键,多个值 55 | if relevance_list in relevance: 56 | if isinstance(relevance_list, list): 57 | a = relevance[relevance_list] 58 | a.append(relevance_value) 59 | relevance[relevance_list] = a 60 | else: 61 | a = relevance[relevance_list] 62 | b = list() 63 | b.append(a) 64 | b.append(relevance_value) 65 | relevance[relevance_list] = a 66 | else: 67 | relevance[relevance_list] = relevance_value 68 | logging.debug("提取后,关联键对象\n%s" % relevance) 69 | return relevance 70 | 71 | 72 | if __name__ == "__main__": 73 | _data = {'code': '999999', 'msg': '成功', 'data': {'first_name': 'Tom', 'last_name': '', 'phone': '123456789', 'email': '123@qq.com', 'key': 'e1dfbfd759ad46bcdc44df989ade3f1c190cc936', 'date_joined': '2018-06-28 02:54:00', 'userphoto': '/file/userphoto.jpg'}} 74 | _relevance = {'name': '10', 'type': 'Web', 'version': 'KHQdpM8gAL', 'description': 'avp7tZOyiY'} 75 | _relevance_list = ['key'] 76 | print(get_relevance(_data, _relevance, _relevance_list)) 77 | 78 | -------------------------------------------------------------------------------- /Common/HostManage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:08 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: HostManage.py 10 | 11 | # @Software: PyCharm 12 | 13 | 14 | import re 15 | 16 | from config.configHost import ConfHost 17 | 18 | 19 | def host_manage(host): 20 | """ 21 | host关联配置 22 | :param host: 23 | :return: 24 | """ 25 | try: 26 | relevance_list = re.findall("\${(.*?)}\$", host) # 查找字符串中所有$key$ 作为关联对象 27 | for n in relevance_list: # 遍历关联key 28 | pattern = re.compile('\${' + n + '}\$') # 初始化正则匹配 29 | # 初始化host配置 30 | host_config = ConfHost() 31 | host_relevance = host_config.get_host_conf() # 获取配置里面所有host 32 | host = re.sub(pattern, host_relevance[n], host, count=1) # 替换host 1次 33 | except TypeError: 34 | pass 35 | return host 36 | 37 | 38 | if __name__ == "__main__": 39 | _custom_fail = "这是一段${test_platform}$用的数据" 40 | _relevance = {"test": "测试"} 41 | print(host_manage(_custom_fail)) 42 | -------------------------------------------------------------------------------- /Common/IniCase.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/10 13:26 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: IniCase.py 10 | 11 | # @Software: PyCharm 12 | import yaml 13 | 14 | 15 | def ini_case(_path): 16 | """ 17 | case初始化步骤 18 | :param _path: case路径 19 | :return: 20 | """ 21 | with open(_path + "/case.yaml", 'r', encoding="utf-8") as load_f: 22 | project_dict = yaml.load(load_f) 23 | return project_dict 24 | 25 | 26 | if __name__ == "__main__": 27 | print(ini_case('D:\\project\\PyTest_allure_apitest\\test_case\\test_01_login')) 28 | 29 | -------------------------------------------------------------------------------- /Common/IniRelevance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 9:33 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: IniRelevance.py 10 | 11 | # @Software: PyCharm 12 | 13 | from config import ConfRelevance 14 | 15 | 16 | def ini_relevance(_path): 17 | """ 18 | 读取初始化关联文件 19 | :param _path: case路径 20 | :return: 21 | """ 22 | rel = ConfRelevance.ConfRelevance(_path + "/relevance.ini") 23 | relevance = rel.get_relevance_conf() 24 | return relevance 25 | 26 | 27 | if __name__ == "__main__": 28 | print(ini_relevance('D:\\project\\PyTest_allure_apitest\\test_case\\test_01_login')) 29 | -------------------------------------------------------------------------------- /Common/MkDir.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/12 10:39 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: MkDir.py 10 | 11 | # @Software: PyCharm 12 | import os 13 | 14 | 15 | def mk_dir(path): 16 | # 去除首位空格 17 | path = path.strip() 18 | # 去除尾部 \ 符号 19 | path = path.rstrip("\\") 20 | 21 | # 判断路径是否存在 22 | # 存在 True 23 | # 不存在 False 24 | is_exists = os.path.exists(path) 25 | 26 | # 判断结果 27 | if not is_exists: 28 | try: 29 | # 如果不存在则创建目录 30 | # 创建目录操作函数 31 | os.makedirs(path) 32 | except Exception as e: 33 | print(e) 34 | else: 35 | # 如果目录存在则不创建,并提示目录已存在 36 | pass 37 | 38 | 39 | if __name__ == "__main__": 40 | mk_dir("./log") 41 | -------------------------------------------------------------------------------- /Common/ParamManage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | 13 | 14 | import re 15 | 16 | from Common.FunctionReplace import function_replace 17 | 18 | 19 | def manage(param, relevance): 20 | """ 21 | 替换关联数据 22 | :param param: 请求头或请求参数或期望 23 | :param relevance: 关联对象 24 | :return: 25 | """ 26 | if isinstance(param, dict): # 判断类型元字典,递归 27 | for key, value in param.items(): # 遍历参数 28 | if isinstance(value, dict): # 判断类型元字典,递归 29 | param[key] = manage(value, relevance) 30 | elif isinstance(value, list): # 判断列表类型,遍历递归 31 | for k, i in enumerate(value): 32 | param[key][k] = manage(i, relevance) 33 | else: # 字符串类型 34 | try: 35 | relevance_list = re.findall("\${(.*?)}\$", value) # 查找字符串中所有$key$ 作为关联对象 36 | relevance_index = 0 # 初始化列表索引 37 | for n in relevance_list: # 遍历关联key 38 | pattern = re.compile('\${' + n + '}\$') # 初始化正则匹配 39 | n = n.lower() 40 | try: 41 | if isinstance(relevance[n], list): # 判断是关联key是否是个list 42 | try: 43 | # 替换第一个匹配到的关联 44 | param[key] = re.sub(pattern, relevance[n][relevance_index], param[key], count=1) 45 | relevance_index += 1 46 | except IndexError: 47 | # 关联值使用完后,初始化索引为0,重新匹配 48 | relevance_index = 0 49 | param[key] = re.sub(pattern, relevance[n][relevance_index], param[key], count=1) 50 | relevance_index += 1 51 | else: 52 | # 关联key是字符串,直接替换 53 | param[key] = re.sub(pattern, relevance[n], param[key], count=1) 54 | except KeyError: 55 | pass 56 | except TypeError: 57 | pass 58 | try: 59 | param[key] = function_replace(param[key]) 60 | except TypeError: 61 | pass 62 | 63 | elif isinstance(param, list): 64 | for k, i in enumerate(param): 65 | param[k] = manage(i, relevance) 66 | else: # 字符串类型 67 | try: 68 | relevance_list = re.findall("\${(.*?)}\$", param) # 查找字符串中所有$key$ 作为关联对象 69 | relevance_index = 0 # 初始化列表索引 70 | for n in relevance_list: # 遍历关联key 71 | pattern = re.compile('\${' + n + '}\$') # 初始化正则匹配 72 | try: 73 | if isinstance(relevance[n], list): # 判断是关联key是否是个list 74 | try: 75 | # 替换第一个匹配到的关联 76 | param = re.sub(pattern, relevance[n][relevance_index], param, count=1) 77 | relevance_index += 1 78 | except IndexError: 79 | # 关联值使用完后,初始化索引为0,重新匹配 80 | relevance_index = 0 81 | param = re.sub(pattern, relevance[n][relevance_index], param, count=1) 82 | relevance_index += 1 83 | else: 84 | # 关联key是字符串,直接替换 85 | param = re.sub(pattern, relevance[n], param) 86 | except KeyError: 87 | pass 88 | except TypeError: 89 | pass 90 | # try: 91 | # param = function_replace(param) 92 | # except TypeError: 93 | # pass 94 | return param 95 | 96 | 97 | _relevance = "" 98 | 99 | 100 | def get_value(data, value): 101 | """ 102 | author 李涛 103 | 获取json中的值 104 | :param data: json数据 105 | :param value: 值 106 | :return: 107 | """ 108 | global _relevance 109 | if isinstance(data, dict): 110 | if value in data: 111 | _relevance = data[value] 112 | else: 113 | for key in data: 114 | _relevance = get_value(data[key], value) 115 | elif isinstance(data, list): 116 | for key in data: 117 | if isinstance(key, dict): 118 | _relevance = get_value(key, value) 119 | break 120 | return _relevance 121 | 122 | 123 | if __name__ == "__main__": 124 | # a = {"token": "test", "a": {"b": "Token $key$ $key$ $key$ $token$"}, "c": "$word$"} 125 | # b = {"key": ["123", "321"], "token": "test"} 126 | # print(manage(a, b)) 127 | c = {"code": 0, "codeMessage": "success", "data": {"phone": "17150194892"}, "ts": 1531460245367} 128 | print(get_value(c, "phone")) 129 | -------------------------------------------------------------------------------- /Common/ReadParam.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:09 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: ReadParam.py 10 | 11 | # @Software: PyCharm 12 | 13 | 14 | import json 15 | from json import JSONDecodeError 16 | 17 | from Common.ParamManage import manage 18 | from main import failureException 19 | 20 | 21 | def read_param(test_name, param, relevance, _path, result): 22 | """ 23 | 判断用例中参数类型 24 | :param test_name: 用例名称 25 | :param param: 26 | :param relevance: 关联对象 27 | :param _path: case路径 28 | :param result: 全局结果 29 | :return: 30 | """ 31 | # 用例中参数为json格式 32 | if isinstance(param, dict): 33 | param = manage(param, relevance) 34 | # 用例中参数非json格式 35 | else: 36 | try: 37 | with open(_path+"/"+param, "r", encoding="utf-8") as f: 38 | data = json.load(f) 39 | for i in data: 40 | # 通过test_name索引,提取第一个匹配到的用例参数 41 | if i["test_name"] == test_name: 42 | param = i["parameter"] 43 | break 44 | # 为空,未匹配到 45 | if not isinstance(param, dict): 46 | result["result"] = False 47 | raise failureException("未找到用例关联的参数\n文件路径: %s\n索引: %s" % (param, test_name)) 48 | else: 49 | param = manage(param, relevance) 50 | except FileNotFoundError: 51 | result["result"] = False 52 | raise failureException("用例关联文件不存在\n文件路径: %s" % param) 53 | except JSONDecodeError: 54 | result["result"] = False 55 | raise failureException("用例关联的参数文件有误\n文件路径: %s" % param) 56 | return param 57 | 58 | 59 | if __name__ == "__main__": 60 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_02_addProject' 61 | _param = {} 62 | _relevance = {'name': '6', 'type': 'Web', 'version': 'gmRu40IPsY', 'description': 'he6xNHtSIO'} 63 | _result = {'result': True} 64 | _test_name = '登陆1' 65 | print(read_param(_test_name, _param, _relevance, path, _result)) 66 | -------------------------------------------------------------------------------- /Common/TestAndCheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 16:05 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: TestAndCheck.py 10 | 11 | # @Software: PyCharm 12 | import allure 13 | 14 | from Common import requestSend, init, CheckResult 15 | 16 | 17 | def api_send_check(case_data, project_dict, relevance, rel, _path, result): 18 | """ 19 | 接口请求并校验结果 20 | :param case_data: 单条用例 21 | :param project_dict: 用例文件对象 22 | :param relevance: 关联值实例对象 23 | :param _path: case目录 24 | :param rel: 关联值类对象 25 | :param result: 全局测试结果 26 | :return: 27 | """ 28 | # 发送请求并获取code, data 29 | code, data = requestSend.send_request(case_data, project_dict["testinfo"].get("host"), 30 | project_dict["testinfo"].get("address"), relevance, _path, result) 31 | # 校验测试结果 32 | with allure.step("校验测试结果"): 33 | pass 34 | if isinstance(case_data["check"], list): 35 | for i in case_data["check"]: 36 | CheckResult.check(case_data["test_name"], i, code, data, relevance, _path, result) 37 | else: 38 | CheckResult.check(case_data["test_name"], case_data["check"], code, data, relevance, _path, result) 39 | # 记录关联值 40 | init.get_relevance(data, case_data["relevance"], rel) 41 | 42 | 43 | if __name__ == "__main__": 44 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_02_addProject' 45 | _case_data = {'test_name': '添加项目', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': [{'check_type': 'no_check', 'datebase': None, 'expected_code': None, 'expected_request': None, 'CustomFail': None}], 'relevance': ['王八大']} 46 | _project_dict = {'testinfo': {'id': 'test_addProject', 'title': '添加项目', 'host': '${test_platform}$', 'address': '/api/project/add_project'}, 'premise': [{'test_name': '登陆1', 'info': '正常登陆1', 'host': '${test_platform}$', 'address': '/api/user/login', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'form-data', 'headers': {}, 'timeout': 8, 'parameter': {'username': 'litao', 'password': 'lt19910301'}, 'file': False, 'relevance': ['key']}], 'test_case': [{'test_name': '添加项目', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': [{'check_type': 'no_check', 'datebase': None, 'expected_code': None, 'expected_request': None, 'CustomFail': None}], 'relevance': ['王八大']}, {'test_name': '添加项目1', 'info': '添加项目1', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '$RandomString(10)$ $RandomString(10)$', 'type': '$Choice(Web,App)list$', 'version': '$RandomInt(10,100)$ $RandomString(10)$', 'description': '$RandomFloat(10,100,5)$ $RandomString(10)$'}, 'file': False, 'check': [{'check_type': 'only_check_status', 'datebase': None, 'expected_code': 200, 'expected_request': None}], 'relevance': ['code']}, {'test_name': '添加项目2', 'info': '添加项目2', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '$GetTime(time_type=now,layout=%Y-%m,unit=5,5,5,5,5)$', 'type': 'Web', 'version': '321', 'description': '123'}, 'file': False, 'check': {'check_type': 'json', 'datebase': None, 'expected_code': 200, 'expected_request': {'code': '999997', 'msg': '存在相同名称', 'data': None}}, 'relevance': ['code']}, {'test_name': '添加项目3', 'info': '添加项目3', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'entirely_check', 'datebase': None, 'expected_code': 200, 'expected_request': {'code': '999997', 'msg': '存在相同名称', 'data': None}}, 'relevance': ['code']}, {'test_name': '添加项目4', 'info': '添加项目4', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'Regular_check', 'datebase': None, 'expected_code': 200, 'expected_request': ['存在相同名称', 'msg']}, 'relevance': ['code']}, {'test_name': '添加项目5', 'info': '添加项目5', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'datebase_check', 'datebase': {'name': 'api_test', 'user': 'root', 'password': 'lt19910301', 'port': 3306, 'sql': 'select * form api_test'}, 'expected_code': 200, 'expected_request': None}, 'relevance': ['code']}]} 47 | _rel = {'name': '9', 'type': 'Web', 'version': 'oKvtDG4Abd', 'description': 'oQDijtqUkr'} 48 | _relevance = {'name': '9', 'type': 'Web', 'version': 'oKvtDG4Abd', 'description': 'oQDijtqUkr', 'key': 'e1dfbfd759ad46bcdc44df989ade3f1c190cc936'} 49 | _result = {'result': True} 50 | api_send_check(_case_data, _project_dict, _relevance, _rel, path, _result) 51 | -------------------------------------------------------------------------------- /Common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/Common/__init__.py -------------------------------------------------------------------------------- /Common/confighttp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | 13 | import json 14 | import logging 15 | 16 | import requests 17 | import simplejson 18 | 19 | 20 | def post(header, address, request_parameter_type, timeout=8, data=None, files=None): 21 | """ 22 | post 请求 23 | :param header: 请求头 24 | :param address: host地址 25 | :param request_parameter_type: 接口请求参数格式 (form-data, raw, Restful) 26 | :param timeout: 超时时间 27 | :param data: 请求参数 28 | :param files: 文件路径 29 | :return: 30 | """ 31 | if request_parameter_type == 'raw': 32 | data = json.dumps(data) 33 | response = requests.post(url=address, data=data, headers=header, timeout=timeout, files=files) 34 | try: 35 | return response.status_code, response.json() 36 | except json.decoder.JSONDecodeError: 37 | return response.status_code, '' 38 | except simplejson.errors.JSONDecodeError: 39 | return response.status_code, '' 40 | except Exception as e: 41 | logging.exception('ERROR') 42 | logging.error(e) 43 | raise 44 | 45 | 46 | def get(header, address, data, timeout=8): 47 | """ 48 | get 请求 49 | :param header: 请求头 50 | :param address: host地址 51 | :param data: 请求参数 52 | :param timeout: 超时时间 53 | :return: 54 | """ 55 | response = requests.get(url=address, params=data, headers=header, timeout=timeout) 56 | if response.status_code == 301: 57 | response = requests.get(url=response.headers["location"]) 58 | try: 59 | return response.status_code, response.json() 60 | except json.decoder.JSONDecodeError: 61 | return response.status_code, '' 62 | except simplejson.errors.JSONDecodeError: 63 | return response.status_code, '' 64 | except Exception as e: 65 | logging.exception('ERROR') 66 | logging.error(e) 67 | raise 68 | 69 | 70 | def put(header, address, request_parameter_type, data=None, timeout=8, files=None): 71 | """ 72 | put 请求 73 | :param header: 请求头 74 | :param address: host地址 75 | :param request_parameter_type: 接口请求参数格式 (form-data, raw, Restful) 76 | :param data: 请求参数 77 | :param timeout: 超时时间 78 | :param files: 文件路径 79 | :return: 80 | """ 81 | if request_parameter_type == 'raw': 82 | data = json.dumps(data) 83 | response = requests.put(url=address, data=data, headers=header, timeout=timeout, files=files) 84 | try: 85 | return response.status_code, response.json() 86 | except json.decoder.JSONDecodeError: 87 | return response.status_code, '' 88 | except simplejson.errors.JSONDecodeError: 89 | return response.status_code, '' 90 | except Exception as e: 91 | logging.exception('ERROR') 92 | logging.error(e) 93 | raise 94 | 95 | 96 | def delete(header, address, data, timeout=8): 97 | """ 98 | put 请求 99 | :param header: 请求头 100 | :param address: host地址 101 | :param timeout: 超时时间 102 | :param data: 请求参数 103 | :return: 104 | """ 105 | response = requests.delete(url=address, params=data, headers=header, timeout=timeout) 106 | try: 107 | return response.status_code, response.json() 108 | except json.decoder.JSONDecodeError: 109 | return response.status_code, '' 110 | except simplejson.errors.JSONDecodeError: 111 | return response.status_code, '' 112 | except Exception as e: 113 | logging.exception('ERROR') 114 | logging.error(e) 115 | raise 116 | 117 | -------------------------------------------------------------------------------- /Common/expectedManage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | 13 | import json 14 | from json import JSONDecodeError 15 | 16 | from Common.ParamManage import manage 17 | from main import failureException 18 | 19 | 20 | def read_json(test_name, code_json, relevance, _path, result): 21 | """ 22 | 校验内容读取 23 | :param test_name: 用例名称,用作索引 24 | :param code_json: 文件路径 25 | :param relevance: 关联对象 26 | :param _path: case路径 27 | :param result: 全局结果 28 | :return: 29 | """ 30 | # 用例中参数为json格式 31 | if isinstance(code_json, dict): 32 | code_json = manage(code_json, relevance) 33 | # 用例中参数非json格式 34 | else: 35 | try: 36 | with open(_path+"/"+code_json, "r", encoding="utf-8") as f: 37 | data = json.load(f) 38 | for i in data: 39 | # 遍历,通过用例名称做索引查找到第一个期望结果后,跳出循环 40 | if i["test_name"] == test_name: 41 | code_json = i["json"] 42 | break 43 | # 如果code_json为空,表示未找到用例关联的期望结果 44 | if not code_json: 45 | result["result"] = False 46 | raise failureException("未找到用例关联的期望结果\n文件路径: %s\n索引: %s" % (code_json, test_name)) 47 | else: 48 | code_json = manage(code_json, relevance) 49 | # 文件不存在 50 | except FileNotFoundError: 51 | result["result"] = False 52 | raise failureException("用例关联文件不存在\n文件路径: %s" % code_json) 53 | # 文件存在,但里面存储的数据有误,json.load执行异常 54 | except JSONDecodeError: 55 | result["result"] = False 56 | raise failureException("用例关联的期望文件有误\n文件路径: %s" % code_json) 57 | # 返回获取的期望结果 58 | return code_json 59 | 60 | 61 | if __name__ == "__main__": 62 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_01_login' 63 | _code_json = 'expected.json' 64 | _relevance = {'name': '123', 'type': 'Web', 'version': '1.2.0', 'description': '这是一个描述', 'first_name': 'Tom'} 65 | _result = {'result': True} 66 | _test_name = '登陆2' 67 | read_json(_test_name, _code_json, _relevance, path, _result) 68 | -------------------------------------------------------------------------------- /Common/init.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import logging 13 | import time 14 | 15 | import allure 16 | 17 | from Common.GetRelevance import get_relevance 18 | from Common.requestSend import send_request 19 | from main import failureException 20 | 21 | 22 | def ini_request(case_dict, relevance, _path, result): 23 | """ 24 | 用例前提条件执行,提取关联键 25 | :param case_dict: 用例对象 26 | :param relevance: 关联对象 27 | :param _path: case路径 28 | :param result: 全局结果 29 | :return: 30 | """ 31 | if isinstance(case_dict["premise"], list): 32 | logging.info("执行测试用例前置接口") 33 | with allure.step("接口关联请求"): 34 | for i in case_dict["premise"]: 35 | relevance_list = relevance.copy() 36 | for j in range(0, 3): 37 | # 获取前置接口关联数据失败 38 | code, data = send_request(i, case_dict["testinfo"].get("host"), 39 | case_dict["testinfo"].get("address"), relevance_list, _path, result) 40 | if not data: 41 | with allure.step("接口请求失败!等待三秒后重试!"): 42 | pass 43 | logging.info("接口请求失败!等待三秒后重试!") 44 | continue 45 | if i["relevance"]: 46 | if len(i["relevance"]): 47 | relevance = get_relevance(data, i["relevance"], relevance) 48 | if isinstance(relevance, bool): 49 | with allure.step("从结果中提取关联键的值失败!等待3秒后重试!"): 50 | pass 51 | logging.info("从结果中提取关联键的值失败!等待3秒后重试!") 52 | time.sleep(3) 53 | continue 54 | else: 55 | break 56 | else: 57 | break 58 | else: 59 | break 60 | if isinstance(relevance, bool): 61 | logging.info("从结果中提取关联键的值失败!重试三次失败") 62 | result["result"] = False 63 | raise failureException("获取前置接口关联数据失败") 64 | return relevance 65 | 66 | 67 | if __name__ == "__main__": 68 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_02_addProject' 69 | _case_dict = {'testinfo': {'id': 'test_addProject', 'title': '添加项目', 'host': '${test_platform}$', 'address': '/api/project/add_project'}, 'premise': [{'test_name': '登陆1', 'info': '正常登陆1', 'host': '${test_platform}$', 'address': '/api/user/login', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'form-data', 'headers': {}, 'timeout': 8, 'parameter': {'username': 'litao', 'password': 'lt19910301'}, 'file': False, 'relevance': ['key']}], 'test_case': [{'test_name': '添加项目', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': [{'check_type': 'no_check', 'datebase': None, 'expected_code': None, 'expected_request': None, 'CustomFail': None}], 'relevance': ['王八大']}, {'test_name': '添加项目1', 'info': '添加项目1', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '$RandomString(10)$ $RandomString(10)$', 'type': '$Choice(Web,App)list$', 'version': '$RandomInt(10,100)$ $RandomString(10)$', 'description': '$RandomFloat(10,100,5)$ $RandomString(10)$'}, 'file': False, 'check': [{'check_type': 'only_check_status', 'datebase': None, 'expected_code': 200, 'expected_request': None}], 'relevance': ['code']}, {'test_name': '添加项目2', 'info': '添加项目2', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '$GetTime(time_type=now,layout=%Y-%m,unit=5,5,5,5,5)$', 'type': 'Web', 'version': '321', 'description': '123'}, 'file': False, 'check': {'check_type': 'json', 'datebase': None, 'expected_code': 200, 'expected_request': {'code': '999997', 'msg': '存在相同名称', 'data': None}}, 'relevance': ['code']}, {'test_name': '添加项目3', 'info': '添加项目3', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'entirely_check', 'datebase': None, 'expected_code': 200, 'expected_request': {'code': '999997', 'msg': '存在相同名称', 'data': None}}, 'relevance': ['code']}, {'test_name': '添加项目4', 'info': '添加项目4', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'Regular_check', 'datebase': None, 'expected_code': 200, 'expected_request': ['存在相同名称', 'msg']}, 'relevance': ['code']}, {'test_name': '添加项目5', 'info': '添加项目5', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'raw', 'headers': {'Authorization': 'Token ${key}$', 'Content-Type': 'application/json'}, 'timeout': 8, 'parameter': {'name': '${name}$', 'type': '${type}$', 'version': '${version}$', 'description': '${description}$'}, 'file': False, 'check': {'check_type': 'datebase_check', 'datebase': {'name': 'api_test', 'user': 'root', 'password': 'lt19910301', 'port': 3306, 'sql': 'select * form api_test'}, 'expected_code': 200, 'expected_request': None}, 'relevance': ['code']}]} 70 | _relevance = {'name': '2', 'type': 'Web', 'version': '7m8uPQOXaz', 'description': 'A038mj4sqv'} 71 | _result = {'result': True} 72 | print(ini_request(_case_dict, _relevance, path, _result)) 73 | 74 | -------------------------------------------------------------------------------- /Common/requestSend.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import logging 13 | 14 | import allure 15 | 16 | from Common import confighttp, HostManage, ReadParam, ParamManage 17 | from main import failureException 18 | 19 | 20 | def send_request(data, host, address, relevance, _path, success): 21 | """ 22 | 再次封装请求 23 | :param data: 测试用例 24 | :param host: 测试地址 25 | :param address: 接口地址 26 | :param relevance: 关联对象 27 | :param _path: case路径 28 | :param success: 全局结果 29 | :return: 30 | """ 31 | logging.info("="*100) 32 | header = ReadParam.read_param(data["test_name"], data["headers"], relevance, _path, success) # 处理请求头 33 | logging.debug("请求头处理结果: %s" % header) 34 | parameter = ReadParam.read_param(data["test_name"], data["parameter"], relevance, _path, success) # 处理请求参数 35 | logging.debug("请求参数处理结果: %s" % header) 36 | try: 37 | # 如果用例中写了host和address,则使用用例中的host和address,若没有则使用全局的 38 | host = data["host"] 39 | except KeyError: 40 | pass 41 | try: 42 | address = data["address"] 43 | except KeyError: 44 | pass 45 | host = HostManage.host_manage(host) # host处理,读取配置文件中的host 46 | address = ParamManage.manage(address, relevance) 47 | logging.debug("host处理结果: %s" % host) 48 | if not host: 49 | raise failureException("接口请求地址为空 %s" % data["headers"]) 50 | logging.info("请求接口:%s" % str(data["test_name"])) 51 | logging.info("请求地址:%s" % data["http_type"] + "://" + host + address) 52 | logging.info("请求头: %s" % str(header)) 53 | logging.info("请求参数: %s" % str(parameter)) 54 | if data["request_type"].lower() == 'post': 55 | if data["file"]: 56 | with allure.step("POST上传文件"): 57 | allure.attach("请求接口:", str(data["test_name"])) 58 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 59 | allure.attach("请求头", str(header)) 60 | allure.attach("请求参数", str(parameter)) 61 | result = confighttp.post(header=header, 62 | address=data["http_type"] + "://" + host + address, 63 | request_parameter_type=data["parameter_type"], files=parameter, 64 | timeout=data["timeout"]) 65 | else: 66 | with allure.step("POST请求接口"): 67 | allure.attach("请求接口:", str(data["test_name"])) 68 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 69 | allure.attach("请求头", str(header)) 70 | allure.attach("请求参数", str(parameter)) 71 | logging.info("POST请求接口") 72 | result = confighttp.post(header=header, address=data["http_type"] + "://" + host + address, 73 | request_parameter_type=data["parameter_type"], data=parameter, 74 | timeout=data["timeout"]) 75 | elif data["request_type"].lower() == 'get': 76 | with allure.step("GET请求接口"): 77 | allure.attach("请求接口:", str(data["test_name"])) 78 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 79 | allure.attach("请求头", str(header)) 80 | allure.attach("请求参数", str(parameter)) 81 | logging.info("GET请求接口") 82 | result = confighttp.get(header=header, address=data["http_type"] + "://" + host + address, 83 | data=parameter, timeout=data["timeout"]) 84 | elif data["request_type"].lower() == "put": 85 | if data["file"]: 86 | with allure.step("PUT上传文件"): 87 | allure.attach("请求接口:", str(data["test_name"])) 88 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 89 | allure.attach("请求头", str(header)) 90 | allure.attach("请求参数", str(parameter)) 91 | logging.info("PUT上传文件") 92 | result = confighttp.put(header=header, 93 | address=data["http_type"] + "://" + host + address, 94 | request_parameter_type=data["parameter_type"], files=parameter, 95 | timeout=data["timeout"]) 96 | else: 97 | with allure.step("PUT请求接口"): 98 | allure.attach("请求接口:", str(data["test_name"])) 99 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 100 | allure.attach("请求头", str(header)) 101 | allure.attach("请求参数", str(parameter)) 102 | logging.info("PUT请求接口") 103 | result = confighttp.put(header=header, address=data["http_type"] + "://" + host + address, 104 | request_parameter_type=data["parameter_type"], data=parameter, 105 | timeout=data["timeout"]) 106 | elif data["request_type"].lower() == "delete": 107 | with allure.step("DELETE请求接口"): 108 | allure.attach("请求接口:", str(data["test_name"])) 109 | allure.attach("请求地址", data["http_type"] + "://" + host + address) 110 | allure.attach("请求头", str(header)) 111 | allure.attach("请求参数", str(parameter)) 112 | logging.info("DELETE请求接口") 113 | result = confighttp.delete(header=header, address=data["http_type"] + "://" + host + address, 114 | data=parameter, timeout=data["timeout"]) 115 | else: 116 | result = {"code": False, "data": False} 117 | logging.info("接口请求结果:\n %s" % str(result)) 118 | return result 119 | 120 | 121 | if __name__ == "__main__": 122 | path = 'D:\\project\\PyTest_allure_apitest\\test_case\\test_02_addProject' 123 | _address = '/api/project/add_project' 124 | _data = {'test_name': '登陆1', 'info': '正常登陆1', 'host': '${test_platform}$', 'address': '/api/user/login', 'http_type': 'http', 'request_type': 'post', 'parameter_type': 'form-data', 'headers': {}, 'timeout': 8, 'parameter': {'username': 'litao', 'password': 'lt19910301'}, 'file': False, 'relevance': ['key']} 125 | _host = '${test_platform}$' 126 | _relevance = {'name': '9', 'type': 'Web', 'version': 'ByTQF7Zmaz', 'description': 'V7GeXKU9E6'} 127 | _success = {'result': True} 128 | print(send_request(_data, _host, _address, _relevance, path, _success)) 129 | -------------------------------------------------------------------------------- /Linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo OFF 3 | 4 | echo .::::::::::::::::::::::::::::::::::::::::::::::::: 5 | 6 | echo .:: 7 | 8 | echo .:: 接口测试 9 | 10 | echo .:: 11 | 12 | echo .:: 作者: 李涛 13 | 14 | echo .:: 15 | 16 | echo .:: 版本 V1.0.0 17 | 18 | echo .:: 19 | 20 | echo .:: 时间 2018.11.10 21 | 22 | echo .:: 23 | 24 | echo .::::::::::::::::::::::::::::::::::::::::::::::::: 25 | 26 | echo .[ INFO ] 运行环境准备 27 | 28 | # 从配置文件中安装环境依赖库i 29 | if [ -f requirements.txt ]; 30 | then 31 | pip install -r requirements.txt 32 | fi 33 | if [ ! -f requirements.txt ]; 34 | then 35 | echo requirements.txt does not exist 36 | fi 37 | 38 | echo .[INFO] 运行脚本 39 | python3 main.py 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/README.md -------------------------------------------------------------------------------- /RandomData/ChoiceData.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/12 19:22 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: ChoiceData.py 10 | 11 | # @Software: PyCharm 12 | import random 13 | 14 | 15 | def choice_data(data): 16 | """ 17 | 获取随机整型数据 18 | :param data: 19 | :return: 20 | """ 21 | _list = data.split(",") 22 | num = random.choice(_list) 23 | return num 24 | 25 | 26 | if __name__ == "__main__": 27 | print(choice_data("200, 100, 5")) 28 | -------------------------------------------------------------------------------- /RandomData/GetTime.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 11:33 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetTime.py 10 | 11 | # @Software: PyCharm 12 | import datetime 13 | import time 14 | 15 | from main import failureException 16 | 17 | 18 | def get_time(time_type, layout, unit="0,0,0,0,0"): 19 | """ 20 | 获取时间 21 | :param time_type: 现在的时间now, 其他时间else_time 22 | :param layout: 10timestamp, 13timestamp, else 时间类型 23 | :param unit: 时间单位: [seconds, minutes, hours, days, weeks] 秒,分,时,天,周 24 | :return: 25 | """ 26 | tim = datetime.datetime.now() 27 | if time_type != "now": 28 | lag = unit.split(",") 29 | try: 30 | tim = tim + datetime.timedelta(seconds=int(lag[0]), minutes=int(lag[1]), 31 | hours=int(lag[2]), days=int(lag[3]), weeks=int(lag[4])) 32 | except ValueError: 33 | raise failureException("获取时间错误,时间单位%s" % unit) 34 | # 获取10位时间戳 35 | if layout == "10timestamp": 36 | tim = tim.strftime('%Y-%m-%d %H:%M:%S') 37 | tim = int(time.mktime(time.strptime(tim, "%Y-%m-%d %H:%M:%S"))) 38 | return tim 39 | # 获取13位时间戳 40 | elif layout == "13timestamp": 41 | tim = tim.strftime('%Y-%m-%d %H:%M:%S') 42 | tim = int(time.mktime(time.strptime(tim, "%Y-%m-%d %H:%M:%S"))) 43 | tim = int(round(tim * 1000)) 44 | return tim 45 | # 按传入格式获取时间 46 | else: 47 | tim = tim.strftime(layout) 48 | return tim 49 | 50 | 51 | if __name__ == "__main__": 52 | print(get_time("else_time", "%Y-%m-%d %H:%M:%S", "5,5,5,5,5")) 53 | print(get_time("now", "%Y-%m-%d %H:%M:%S", "5,5,5,5,5")) 54 | 55 | -------------------------------------------------------------------------------- /RandomData/Md5Data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/14 15:11 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: Md5Data.py 10 | 11 | # @Software: PyCharm 12 | 13 | import hashlib 14 | import re 15 | 16 | 17 | def md5_data(data): 18 | """ 19 | md5加密 20 | :param data: 21 | :return: 22 | """ 23 | m1 = hashlib.md5() 24 | m1.update(data.encode("utf-8")) 25 | data = m1.hexdigest() 26 | return data 27 | 28 | 29 | def re_md5(data): 30 | test = re.findall("\$MD5\(((?!.*\$MD5\().*?)\)MD5\$", data) 31 | if not len(test): 32 | pass 33 | else: 34 | for i in test: 35 | k = md5_data(i) 36 | i = i.replace("(", "\(").replace(")", "\)") 37 | pattern = re.compile('\$MD5\(' + i + '\)MD5\$') # 初始化正则匹配 38 | data = re.sub(pattern, k, data, count=1) 39 | # print(data) 40 | data = re_md5(data) 41 | return data 42 | 43 | 44 | if __name__ == "__main__": 45 | print(md5_data("123456")) 46 | -------------------------------------------------------------------------------- /RandomData/RandomFloat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 10:58 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: RandomFloat.py 10 | 11 | # @Software: PyCharm 12 | 13 | import random 14 | 15 | from main import failureException 16 | 17 | 18 | def random_float(data): 19 | """ 20 | 获取随机整型数据 21 | :param data: 22 | :return: 23 | """ 24 | try: 25 | start_num, end_num, accuracy = data.split(",") 26 | start_num = int(start_num) 27 | end_num = int(end_num) 28 | accuracy = int(accuracy) 29 | except ValueError: 30 | raise failureException("调用随机整数失败,范围参数或精度有误!\n小数范围精度 %s" % data) 31 | if start_num <= end_num: 32 | num = random.uniform(start_num, end_num) 33 | else: 34 | num = random.uniform(end_num, start_num) 35 | num = round(num, accuracy) 36 | return num 37 | 38 | 39 | if __name__ == "__main__": 40 | print(random_float("200, 100, 5")) 41 | -------------------------------------------------------------------------------- /RandomData/RandomInt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 10:37 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: RandomInt.py 10 | 11 | # @Software: PyCharm 12 | import random 13 | 14 | from main import failureException 15 | 16 | 17 | def random_int(scope): 18 | """ 19 | 获取随机整型数据 20 | :param scope: 时间范围 21 | :return: 22 | """ 23 | try: 24 | start_num, end_num = scope.split(",") 25 | start_num = int(start_num) 26 | end_num = int(end_num) 27 | except ValueError: 28 | raise failureException("调用随机整数失败,范围参数有误!\n %s" % str(scope)) 29 | if start_num <= end_num: 30 | num = random.randint(start_num, end_num) 31 | else: 32 | num = random.randint(end_num, start_num) 33 | return num 34 | 35 | 36 | if __name__ == "__main__": 37 | print(random_int("200,100")) 38 | -------------------------------------------------------------------------------- /RandomData/RandomString.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 11:07 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: RandString.py 10 | 11 | # @Software: PyCharm 12 | import random 13 | import string 14 | 15 | from main import failureException 16 | 17 | 18 | def random_string(num_length): 19 | """ 20 | 从a-zA-Z0-9生成指定数量的随机字符 21 | :param num_length: 字符串长度 22 | :return: 23 | """ 24 | try: 25 | num_length = int(num_length) 26 | except ValueError: 27 | raise failureException("从a-zA-Z0-9生成指定数量的随机字符失败!长度参数有误 %s" % num_length) 28 | num = ''.join(random.sample(string.ascii_letters + string.digits, num_length)) 29 | return num 30 | 31 | 32 | if __name__ == "__main__": 33 | print(random_string(5)) 34 | -------------------------------------------------------------------------------- /RandomData/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/11 10:37 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: __init__.py.py 10 | 11 | # @Software: PyCharm -------------------------------------------------------------------------------- /TestCase/TestAddProject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestAddProject/__init__.py -------------------------------------------------------------------------------- /TestCase/TestAddProject/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_addProject # 用例ID, 用于识别 string 4 | title: 添加项目 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_platform}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: /api/project/add_project # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | - test_name: 登陆1 # 必填 parameter为文件路径时 string 11 | info: 正常登陆1 # 选填 string 12 | host: ${test_platform}$ # 基本信息中若填写,此处为选填 string 13 | address: /api/user/login # 请求接口 string 14 | http_type: http # 请求协议 string 15 | request_type: post # 请求方式 string 16 | parameter_type: form-data # 参数类型 string 17 | headers: {} # 请求头 dict 18 | timeout: 8 # 超时时间 int 19 | parameter: # 可填实际传递参数,若参数过多,可保存在相应的参数文件中,用test_name作为索引 string or dict 20 | username: litao 21 | password: lt19910301 22 | file: false # 是否上传文件,默认false,若上传文件接口,此处为文件相对路径 bool or string 23 | relevance: # 关联的键 list or string 24 | - key 25 | # 测试用例 26 | test_case: 27 | # 第一条case,info可不填 28 | - test_name: 添加项目 # 必填,parameter为文件路径时 29 | http_type: http # 请求协议 30 | request_type: post # 请求类型 31 | parameter_type: raw # 参数类型 32 | headers: # 请求头 33 | Authorization: Token ${key}$ 34 | Content-Type: application/json 35 | timeout: 8 36 | parameter: 37 | name: ${name}$ 38 | type: ${type}$ 39 | version: ${version}$ 40 | description: ${description}$ 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: no_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 46 | expected_request: 47 | CustomFail: # 自定义失败说明 48 | relevance: 49 | - 王八大 # 关联键 50 | 51 | - test_name: 添加项目1 52 | info: 添加项目1 53 | http_type: http 54 | request_type: post 55 | parameter_type: raw 56 | headers: 57 | Authorization: Token ${key}$ 58 | Content-Type: application/json 59 | timeout: 8 60 | parameter: 61 | name: $RandomString(10)$ $RandomString(10)$ 62 | type: $Choice(Web,App)list$ 63 | version: $RandomInt(10,100)$ $RandomString(10)$ 64 | description: $RandomFloat(10,100,5)$ $RandomString(10)$ 65 | file: false 66 | check: 67 | - check_type: only_check_status # 校验http状态 expected_code 必填 68 | datebase: 69 | expected_code: 200 70 | expected_request: 71 | relevance: 72 | - code 73 | 74 | - test_name: 添加项目2 75 | info: 添加项目2 76 | http_type: http 77 | request_type: post 78 | parameter_type: raw 79 | headers: 80 | Authorization: Token ${key}$ 81 | Content-Type: application/json 82 | timeout: 8 83 | parameter: 84 | name: $GetTime(time_type=now,layout=%Y-%m,unit=5,5,5,5,5)$ 85 | type: "Web" 86 | version: "321" 87 | description: "123" 88 | file: false 89 | check: 90 | check_type: json # 校验json格式 expected_code, expected_request 必填 91 | datebase: 92 | expected_code: 200 93 | expected_request: {"code":"999997","msg":"存在相同名称","data":null} # string or dict 94 | relevance: 95 | - code 96 | 97 | - test_name: 添加项目3 98 | info: 添加项目3 99 | http_type: http 100 | request_type: post 101 | parameter_type: raw 102 | headers: 103 | Authorization: Token ${key}$ 104 | Content-Type: application/json 105 | timeout: 8 106 | parameter: 107 | name: ${name}$ 108 | type: ${type}$ 109 | version: ${version}$ 110 | description: ${description}$ 111 | file: false 112 | check: 113 | check_type: entirely_check # 完全校验 expected_code, expected_request 必填 114 | datebase: 115 | expected_code: 200 116 | expected_request: {"code":"999997","msg":"存在相同名称","data":null} # string or dict 117 | relevance: 118 | - code 119 | 120 | - test_name: 添加项目4 121 | info: 添加项目4 122 | http_type: http 123 | request_type: post 124 | parameter_type: raw 125 | headers: 126 | Authorization: Token ${key}$ 127 | Content-Type: application/json 128 | timeout: 8 129 | parameter: 130 | name: ${name}$ 131 | type: ${type}$ 132 | version: ${version}$ 133 | description: ${description}$ 134 | file: false 135 | check: 136 | check_type: Regular_check # 正则校验 expected_code, expected_request 必填 137 | datebase: 138 | expected_code: 200 139 | expected_request: # string or list 140 | - 存在相同名称 141 | - msg 142 | relevance: # list 143 | - code 144 | 145 | - test_name: 添加项目5 146 | info: 添加项目5 147 | http_type: http 148 | request_type: post 149 | parameter_type: raw 150 | headers: 151 | Authorization: Token ${key}$ 152 | Content-Type: application/json 153 | timeout: 8 154 | parameter: 155 | name: ${name}$ 156 | type: ${type}$ 157 | version: ${version}$ 158 | description: ${description}$ 159 | file: false 160 | check: 161 | check_type: datebase_check # 数据库校验 162 | datebase: 163 | name: api_test 164 | user: root 165 | password: lt19910301 166 | port: 3306 167 | sql: select * form api_test 168 | expected_code: 200 169 | expected_request: 170 | relevance: 171 | - code 172 | -------------------------------------------------------------------------------- /TestCase/TestAddProject/expected.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", 4 | "json": { 5 | "code": "999999", 6 | "msg": "成功" 7 | } 8 | }, 9 | { 10 | "test_name": "登陆4", 11 | "json": { 12 | "code": "999999", 13 | "msg": "成功" 14 | } 15 | }, 16 | { 17 | "test_name": "登陆2", 18 | "json": { 19 | "code": "999999", 20 | "msg": "成功" 21 | } 22 | } 23 | ] -------------------------------------------------------------------------------- /TestCase/TestAddProject/logs/2018-11-13.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestAddProject/logs/2018-11-13.log -------------------------------------------------------------------------------- /TestCase/TestAddProject/logs/2018-11-13_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestAddProject/logs/2018-11-13_error.log -------------------------------------------------------------------------------- /TestCase/TestAddProject/logs/2018-11-15.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestAddProject/logs/2018-11-15.log -------------------------------------------------------------------------------- /TestCase/TestAddProject/logs/2018-11-15_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestAddProject/logs/2018-11-15_error.log -------------------------------------------------------------------------------- /TestCase/TestAddProject/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", 4 | "parameter": { 5 | "code": "999999", 6 | "msg": "成功" 7 | } 8 | }, 9 | { 10 | "test_name": "登陆4", 11 | "parameter": { 12 | "code": "999999", 13 | "msg": "成功" 14 | } 15 | }, 16 | { 17 | "test_name": "登陆2", 18 | "parameter": { 19 | "code": "999999", 20 | "msg": "成功" 21 | } 22 | } 23 | ] -------------------------------------------------------------------------------- /TestCase/TestAddProject/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | name=$RandomInt(0,10)$ 3 | type=Web 4 | version=$RandomString(10)$ 5 | description=$RandomString(10)$ -------------------------------------------------------------------------------- /TestCase/TestAddProject/test_AddProject.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | case_dict = ini_case(PATH) 18 | 19 | 20 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 21 | class TestAddProject: 22 | 23 | @classmethod 24 | def setup_class(cls): 25 | cls.rel = ini_relevance(PATH) 26 | cls.result = {"result": True} 27 | 28 | def setup(self): 29 | self.relevance = self.rel.copy() 30 | self.relevance = init.ini_request(case_dict, self.relevance, PATH, self.result) 31 | 32 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 33 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 34 | @allure.story("添加项目") 35 | @allure.issue("http://www.baidu.com") # bug地址 36 | @allure.testcase("http://www.testlink.com") # 用例连接地址 37 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 38 | def test_add_project(self, case_data, login): 39 | """ 40 | 添加项目测试 # 第一条用例描述 41 | :param case_data: 参数化用例的形参 42 | :return: 43 | """ 44 | # 参数化修改test_add_project 注释 45 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 46 | try: 47 | if case_data == v: 48 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 49 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 50 | except IndexError: 51 | pass 52 | # 发送测试请求 53 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 54 | 55 | 56 | if __name__ == "__main__": 57 | LogConfig(PATH) 58 | pytest.main() 59 | -------------------------------------------------------------------------------- /TestCase/TestLogin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestLogin/__init__.py -------------------------------------------------------------------------------- /TestCase/TestLogin/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_login # 用例ID, 用于识别 4 | title: 登陆 # 用例标题,在报告中作为一级目录显示 5 | host: ${test_platform}$ # 请求的域名,可写死,也可写成模板关联host配置文件 6 | address: /api/user/login # 请求地址 7 | 8 | premise: 9 | 10 | test_case: 11 | - test_name: 登陆1 12 | info: 正常登陆1 13 | http_type: http 14 | request_type: post 15 | parameter_type: form-data 16 | headers: {} 17 | timeout: 8 18 | parameter: param.json 19 | file: false 20 | check: 21 | check_type: no_check # 不校验 22 | datebase: 23 | expected_code: 24 | expected_request: 25 | CustomFail: 26 | relevance: 27 | 28 | - test_name: 登陆2 29 | info: 正常登陆2 30 | http_type: http 31 | request_type: post 32 | parameter_type: form-data 33 | headers: {} 34 | timeout: 8 35 | parameter: 36 | username: litao 37 | password: lt19910301 38 | file: false 39 | check: 40 | - check_type: only_check_status # 校验http状态 41 | datebase: 42 | expected_code: 200 43 | expected_request: 44 | 45 | - check_type: json # 校验json格式 46 | datebase: 47 | expected_code: 200 48 | expected_request: expected.json 49 | relevance: 50 | 51 | - test_name: 登陆3 52 | info: 正常登陆3 53 | http_type: http 54 | request_type: post 55 | parameter_type: form-data 56 | headers: {} 57 | timeout: 8 58 | parameter: 59 | username: litao 60 | password: lt19910301 61 | file: false 62 | check: 63 | check_type: json # 校验json格式 64 | datebase: 65 | expected_code: 200 66 | expected_request: expected.json 67 | relevance: 68 | 69 | - test_name: 登陆4 70 | info: 正常登陆4 71 | http_type: http 72 | request_type: post 73 | parameter_type: form-data 74 | headers: {} 75 | timeout: 8 76 | parameter: 77 | username: litao 78 | password: lt19910301 79 | file: false 80 | check: 81 | check_type: entirely_check # 完全校验 82 | datebase: 83 | expected_code: 200 84 | expected_request: expected.json 85 | relevance: 86 | 87 | - test_name: 登陆5 88 | info: 正常登陆5 89 | http_type: http 90 | request_type: post 91 | parameter_type: form-data 92 | headers: {} 93 | timeout: 8 94 | parameter: 95 | username: litao 96 | password: lt19910301 97 | file: false 98 | check: 99 | check_type: Regular_check # 正则校验 100 | datebase: 101 | expected_code: 200 102 | expected_request: 103 | - 成功 104 | relevance: 105 | 106 | - test_name: 登陆6 107 | info: 正常登陆6 108 | http_type: http 109 | request_type: post 110 | parameter_type: form-data 111 | headers: {} 112 | timeout: 8 113 | parameter: 114 | username: litao 115 | password: lt19910301 116 | file: false 117 | check: 118 | check_type: datebase_check # 数据库校验 119 | datebase: 120 | NAME: api_test 121 | USER: root 122 | PASSWORD: lt19910301 123 | PORT: 3306 124 | sql: select * form api_test 125 | expected_code: 200 126 | expected_request: 127 | relevance: 128 | -------------------------------------------------------------------------------- /TestCase/TestLogin/expected.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", "json": {"code":"999999","msg":"成功","data":{"first_name":"${first_name}$","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 4 | }, 5 | { 6 | "test_name": "登陆4", "json": {"code":"999999","msg":"成功","data":{"first_name":"${first_name}$","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 7 | }, 8 | { 9 | "test_name": "登陆2", "json": {"code":"999999","msg":"成功","data":{"first_name":"${first_name}$","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 10 | } 11 | ] -------------------------------------------------------------------------------- /TestCase/TestLogin/logs/2018-11-13.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestLogin/logs/2018-11-13.log -------------------------------------------------------------------------------- /TestCase/TestLogin/logs/2018-11-13_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestLogin/logs/2018-11-13_error.log -------------------------------------------------------------------------------- /TestCase/TestLogin/logs/2018-11-15.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestLogin/logs/2018-11-15.log -------------------------------------------------------------------------------- /TestCase/TestLogin/logs/2018-11-15_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestLogin/logs/2018-11-15_error.log -------------------------------------------------------------------------------- /TestCase/TestLogin/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆1", "parameter":{"username": "litao", "password": "lt19910301"} 4 | }, 5 | { 6 | "test_name": "测试", "parameter":{} 7 | }, 8 | { 9 | "test_name": "测试", "parameter":{} 10 | }, 11 | { 12 | "test_name": "测试", "parameter":{} 13 | }, 14 | { 15 | "test_name": "测试", "parameter":{} 16 | } 17 | ] -------------------------------------------------------------------------------- /TestCase/TestLogin/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | name=123 3 | type=Web 4 | version=1.2.0 5 | description=这是一个描述 6 | first_name=Tom -------------------------------------------------------------------------------- /TestCase/TestLogin/test_login.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | case_dict = ini_case(PATH) 18 | 19 | 20 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 21 | class TestLogin: 22 | 23 | @classmethod 24 | def setup_class(cls): 25 | cls.rel = ini_relevance(PATH) 26 | cls.result = {"result": True} 27 | 28 | def setup(self): 29 | self.relevance = self.rel.copy() 30 | self.relevance = init.ini_request(case_dict, self.relevance, PATH, self.result) 31 | 32 | # @pytest.mark.skipif(fa) # 跳过条件 33 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 34 | @allure.story("登录") 35 | @allure.issue("http://www.baidu.com") # bug地址 36 | @allure.testcase("http://www.testlink.com") # 用例连接地址 37 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 38 | def test_login(self, case_data): 39 | """ 40 | 正常登陆 # 第一条用例描述 41 | :param case_data: 参数化用例的形参 42 | :return: 43 | """ 44 | # 参数化修改test_add_project 注释 45 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 46 | try: 47 | if case_data == v: 48 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 49 | TestLogin.test_login.__doc__ = case_dict["test_case"][k+1]["info"] 50 | except IndexError: 51 | pass 52 | # 发送测试请求 53 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 54 | 55 | 56 | if __name__ == "__main__": 57 | LogConfig(PATH) 58 | pytest.main("test_login.py") 59 | -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/TestSmallNumberBindUnbind/__init__.py -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_samllNumber 4 | title: 小号绑定/解绑 5 | 6 | # 前置条件,case之前需关联的接口 7 | premise: 8 | 9 | # 测试用例 10 | test_case: 11 | - test_name: 绑定小号 12 | info: 按订单号绑定 13 | http_type: http 14 | host: ${mock}$ 15 | address: /mock/13/api/newOrderLogisticsPhone/bindPhone 16 | request_type: post 17 | parameter_type: raw 18 | headers: 19 | Content-Type: application/json 20 | timeout: 8 21 | parameter: 22 | orderBn: "2018118195340" 23 | file: false 24 | check: 25 | check_type: json # json校验 26 | datebase: 27 | expected_code: 200 28 | expected_request: { 29 | "code": "0", 30 | "codeMessage": "success", 31 | "data": { 32 | "phone": "17150194892" 33 | }, 34 | "ts": "1531460245367" 35 | } 36 | relevance: 37 | - phone 38 | 39 | - test_name: 解绑小号 40 | info: 解绑订单绑定的小号 41 | http_type: http 42 | host: ${mock}$ 43 | address: /mock/13/api/newOrderLogisticsPhone/unbind 44 | request_type: post 45 | parameter_type: raw 46 | headers: 47 | Content-Type: application/json 48 | timeout: 8 49 | parameter: 50 | orderBn: "2018118195340" 51 | phone: ${phone}$ 52 | smallNumber: "" 53 | appId: "" 54 | token: "" 55 | file: false 56 | check: 57 | check_type: Regular_check 58 | datebase: 59 | expected_code: 200 60 | expected_request: 61 | - '"result": True' 62 | - '"result": True' 63 | relevance: 64 | 65 | - test_name: 绑定小号 66 | info: 按会员id绑定小号 67 | http_type: http 68 | host: ${mock}$ 69 | address: /mock/13/api/newOrderLogisticsPhone/bindPhoneByMembersId 70 | request_type: post 71 | parameter_type: raw 72 | headers: 73 | Content-Type: application/json 74 | timeout: 8 75 | parameter: 76 | memberId: "132" 77 | phone: "Web" 78 | channelCode: "321" 79 | appId: "123" 80 | account: "" 81 | token: "" 82 | file: false 83 | check: 84 | check_type: json # 校验json格式 85 | datebase: 86 | expected_code: 200 87 | expected_request: { 88 | "code": "0", 89 | "codeMessage": "success", 90 | "data": { 91 | "phone": "17150194892" 92 | }, 93 | "ts": "1531460245367" 94 | } 95 | relevance: 96 | - phone 97 | 98 | - test_name: 解绑小号 99 | info: 解绑会员ID绑定的小号 100 | http_type: http 101 | host: ${mock}$ 102 | address: /mock/13/api/newOrderLogisticsPhone/unbind 103 | request_type: post 104 | parameter_type: raw 105 | headers: 106 | Content-Type: application/json 107 | timeout: 8 108 | parameter: 109 | orderBn: "2018118195340" 110 | phone: $phone$ 111 | smallNumber: "" 112 | appId: "" 113 | token: "" 114 | file: false 115 | check: 116 | check_type: Regular_check # 校验http状态 117 | datebase: 118 | expected_code: 200 119 | expected_request: "'result': True" 120 | relevance: -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/expected.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 4 | }, 5 | { 6 | "test_name": "登陆4", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 7 | }, 8 | { 9 | "test_name": "登陆2", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 10 | } 11 | ] -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆1", "parameter":{"username": "litao", "password": "lt19910301"} 4 | }, 5 | { 6 | "test_name": "测试", "parameter":{} 7 | }, 8 | { 9 | "test_name": "测试", "parameter":{} 10 | }, 11 | { 12 | "test_name": "测试", "parameter":{} 13 | }, 14 | { 15 | "test_name": "测试", "parameter":{} 16 | } 17 | ] -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | -------------------------------------------------------------------------------- /TestCase/TestSmallNumberBindUnbind/test_smalNumber.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | case_dict = ini_case(PATH) 18 | 19 | 20 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 21 | class TestAddProject: 22 | 23 | @classmethod 24 | def setup_class(cls): 25 | cls.rel = ini_relevance(PATH) 26 | cls.result = {"result": True} 27 | 28 | def setup(self): 29 | self.relevance = self.rel.copy() 30 | self.relevance = init.ini_request(case_dict, self.relevance, PATH, self.result) 31 | 32 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 33 | @allure.story("小号绑定/解绑") 34 | def test_add_project(self, case_data): 35 | """ 36 | 按订单号绑定 37 | :param case_data: 38 | :return: 39 | """ 40 | # 参数化修改test_login 注释 41 | for k, v in enumerate(case_dict["test_case"]): 42 | try: 43 | if case_data == v: 44 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 45 | except IndexError: 46 | pass 47 | # 发送测试请求 48 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 49 | 50 | 51 | if __name__ == "__main__": 52 | LogConfig(PATH) 53 | pytest.main() 54 | -------------------------------------------------------------------------------- /TestCase/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestCase/__init__.py -------------------------------------------------------------------------------- /TestScene/Test_Refund/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/Test_Refund/__init__.py -------------------------------------------------------------------------------- /TestScene/Test_Refund/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_04_ModifySend # 用例ID, 用于识别 string 4 | title: 收单到退货的场景用例 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 8 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: pos推送物流接口 # 必填,parameter为文件路径时 32 | info: pos推送物流接口 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: raw # 参数类型 36 | address: /centerDocker/api/orderLogistics/pushOrderLogistics?shopBn=meituan×tamp=20190716190000&sign=509ea0b24c71857122c1f2d2f17155af&isRightNow=off 37 | headers: # 请求头 38 | Content-Type: application/json 39 | timeout: 8 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: 处理成功 47 | relevance: # 关联键 48 | 49 | - test_name: 订单状态接口反写 # 必填,parameter为文件路径时 50 | info: 订单状态接口反写 51 | http_type: http # 请求协议 52 | request_type: post # 请求类型 53 | parameter_type: raw # 参数类型 54 | address: /centerDocker/api/orderStatus/writeOrderStatus?shopBn=meituan×tamp=20190716190000&sign=509ea0b24c71857122c1f2d2f17155af&isRightNow=off 55 | headers: # 请求头 56 | Content-Type: application/json 57 | timeout: 8 58 | parameter: param.json 59 | file: false 60 | check: # 校验列表 list or dict 61 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 62 | datebase: 63 | expected_code: 200 64 | expected_request: 处理成功 65 | relevance: # 关联键 66 | 67 | - test_name: 退货订单信息处理接口 # 必填,parameter为文件路径时 68 | info: 退货订单信息处理接口 69 | http_type: http # 请求协议 70 | request_type: post # 请求类型 71 | parameter_type: raw # 参数类型 72 | address: /centerDocker/api/returnGoods/pushReturnGoodsToDb?shopBn=meituan×tamp=20190716190000&sign=509ea0b24c71857122c1f2d2f17155af&isRightNow=off 73 | headers: # 请求头 74 | Content-Type: application/json 75 | timeout: 8 76 | parameter: param.json 77 | file: false 78 | check: # 校验列表 list or dict 79 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 80 | datebase: 81 | expected_code: 200 82 | expected_request: 处理成功 83 | relevance: # 关联键 84 | 85 | - test_name: 退货单审核状态反写接口 # 必填,parameter为文件路径时 86 | info: 退货单审核状态反写接口 87 | http_type: http # 请求协议 88 | request_type: post # 请求类型 89 | parameter_type: raw # 参数类型 90 | address: /centerDocker/api/returnGoodsStatus/reverseReturnGoodsStatus?shopBn=meituan×tamp=20190716190000&sign=509ea0b24c71857122c1f2d2f17155af&isRightNow=off 91 | headers: # 请求头 92 | Content-Type: application/json 93 | timeout: 8 94 | parameter: param.json 95 | file: false 96 | check: # 校验列表 list or dict 97 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 98 | datebase: 99 | expected_code: 200 100 | expected_request: 处理成功 101 | relevance: # 关联键 102 | 103 | -------------------------------------------------------------------------------- /TestScene/Test_Refund/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/Test_Refund/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "新快喝", 6 | "to_node_id": "1488934732", 7 | "shopBn": "${shopbn}$", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "1016865", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "1012217", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "6558EAFB4CE824F4EC467DE4DE5EBBC3", 106 | "work_routine": null, 107 | "members_phone": "8B84CDDD502888900C984C1956016BE8", 108 | "storeName": null, 109 | "memberAccount": null, 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | }, 128 | { 129 | "test_name":"pos推送物流接口","parameter":{ 130 | "orderBn": "${order_bn}$", 131 | "shopBn": "${shopbn}$", 132 | "deliveryBn": "${deliveryBn}$", 133 | "deliveryType": "1", 134 | "logisticBns": ["7878241010442","7878241010443"], 135 | "logisticCompany": "2000387", 136 | "logisticStatus": "4", 137 | "dealTime": 1466391287000, 138 | "createtime": 1466391287000, 139 | "operatorName": "admin" 140 | } 141 | }, 142 | { 143 | "test_name":"订单状态接口反写","parameter":{ 144 | "orderStatusCode": "001", 145 | "orderBn": "${order_bn}$", 146 | "shopBn": "${shopbn}$", 147 | "deliveryBn": "${deliveryBn}$", 148 | "sendQuantity": "1", 149 | "operatorName": "11dsfjk", 150 | "modifytime": "2018-11-09" 151 | } 152 | }, 153 | { 154 | "test_name":"退货订单信息处理接口","parameter":{ 155 | "id": null, 156 | "returnGoodsBn": "${returnGoodsBn}$", 157 | "orderBn": "${order_bn}$", 158 | "refundBn": "266565884844", 159 | "orderSource": "新快喝", 160 | "shopBn": "${shopbn}$", 161 | "orderStatus": "finish", 162 | "orderTotalAmount": 789.000, 163 | "orderCostAmount": 1256.000, 164 | "orderPmtOrder": 479.000, 165 | "orderPmtPlateform": 0, 166 | "orderPayed": 144, 167 | "returnAmount": 789.000, 168 | "returnReason": "反倒是", 169 | "returnExplain": "而发生", 170 | "customMark": null, 171 | "returnProductInfos": [ 172 | { 173 | "id": 8557666, 174 | "productName": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 175 | "quantity": 1, 176 | "gPrice": 1198.000, 177 | "unit": null, 178 | "specification": null, 179 | "productBn": "1016865", 180 | "orderBn": null, 181 | "returnGoodsId": null, 182 | "createTime": null, 183 | "returnGoodsBn": null 184 | }, 185 | { 186 | "id": 8557666, 187 | "productName": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 188 | "quantity": 1, 189 | "gPrice": 58.000, 190 | "unit": null, 191 | "specification": null, 192 | "productBn": "1012217", 193 | "orderBn": null, 194 | "returnGoodsId": null, 195 | "createTime": null, 196 | "returnGoodsBn": null 197 | } 198 | ], 199 | "applyStatus": "0", 200 | "name": "commodo", 201 | "zip": null, 202 | "areaState": null, 203 | "areaCity": null, 204 | "areaDistrict": null, 205 | "addr": null, 206 | "mobile": null, 207 | "telephone": null, 208 | "storeNo": "J0S1", 209 | "storeAddress": null, 210 | "platformSource": "新快喝", 211 | "operatorName": "7878", 212 | "createtime": "2018-11-10", 213 | "applyTime": "2018-11-10", 214 | "logisticBn": null, 215 | "logisticCompany": null, 216 | "costShippig": null, 217 | "isProtect": null, 218 | "costProtect": null, 219 | "returnGoodsType": 0, 220 | "returnGoodsProve": null, 221 | "payType": null, 222 | "accountBank": null, 223 | "accountNo": null, 224 | "refundState": null, 225 | "refundCity": null, 226 | "isAutoCreateRefund": null, 227 | "sellerCode": null, 228 | "sellerName": null 229 | } 230 | }, 231 | { 232 | "test_name":"退货单审核状态反写接口", "parameter":{ 233 | "id": null, 234 | "orderBn": "${order_bn}$", 235 | "shopBn": "${shopbn}$", 236 | "returnGoodsBn": "${returnGoodsBn}$", 237 | "operatorName": "sdfsdf", 238 | "status": 1, 239 | "reson": "dolor", 240 | "operatorTime": "2018-11-09" 241 | } 242 | } 243 | ] -------------------------------------------------------------------------------- /TestScene/Test_Refund/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=111120021 3 | shopbn=newkhronghe 4 | deliveryBn=454545467787019a 5 | returnGoodsBn=559949411118 6 | -------------------------------------------------------------------------------- /TestScene/Test_Refund/test_Refundt.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/__init__.py -------------------------------------------------------------------------------- /TestScene/test_IdBinding/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_IdBinding/__init__.py -------------------------------------------------------------------------------- /TestScene/test_IdBinding/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_06_unbunding # 用例ID, 用于识别 string 4 | title: 收单到会员Id绑定 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 根据会员ID和主叫号码绑定小号 # 必填,parameter为文件路径时 32 | info: 根据会员ID和主叫号码绑定小号 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: raw # 参数类型 36 | address: /centerDocker/api/newOrderLogisticsPhone/bindPhoneByMembersId?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 37 | headers: # 请求头 38 | Content-Type: application/json 39 | timeout: 20 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: success 47 | relevance: # 关联键 48 | 49 | -------------------------------------------------------------------------------- /TestScene/test_IdBinding/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_IdBinding/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "天猫", 6 | "to_node_id": "1488934732", 7 | "shopBn": "${shopbn}$", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "1016865", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "1012217", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "test", 106 | "work_routine": null, 107 | "members_phone": null, 108 | "storeName": null, 109 | "memberAccount": "17761272084", 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | 128 | }, 129 | { 130 | "test_name": "根据会员ID和主叫号码绑定小号", "parameter": { 131 | "memberId": "2099739", 132 | "phone": "17761272084", 133 | "channelCode": "pos", 134 | "storeNo": null, 135 | "appId": "pos", 136 | "account": "1232sdf", 137 | "token": "4e634b8a8e5e4a1316b60ca90cf51f81" 138 | } 139 | } 140 | ] -------------------------------------------------------------------------------- /TestScene/test_IdBinding/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=111122abdd 3 | shopbn=newkhronghe 4 | 5 | 6 | -------------------------------------------------------------------------------- /TestScene/test_IdBinding/test_IdBinding.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_ModifySend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_ModifySend/__init__.py -------------------------------------------------------------------------------- /TestScene/test_ModifySend/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_04_ModifySend # 用例ID, 用于识别 string 4 | title: 收单到门店改派的场景用例 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 门店改派接口 # 必填,parameter为文件路径时 32 | info: 门店改派接口 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: raw # 参数类型 36 | address: /centerDocker/api/storeReassign/reassignHandler?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 37 | headers: # 请求头 38 | Content-Type: application/json 39 | timeout: 20 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: 处理成功 47 | relevance: # 关联键 48 | 49 | 50 | -------------------------------------------------------------------------------- /TestScene/test_ModifySend/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_ModifySend/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "新快喝", 6 | "to_node_id": "1488934732", 7 | "shopBn": "${shopbn}$", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "2000021", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "2000022", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "6558EAFB4CE824F4EC467DE4DE5EBBC3", 106 | "work_routine": null, 107 | "members_phone": "8B84CDDD502888900C984C1956016BE8", 108 | "storeName": null, 109 | "memberAccount": null, 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | }, 128 | { 129 | "test_name":"门店改派接口","parameter":{ 130 | "id": 77829560, 131 | "orderBn": "${order_bn}$", 132 | "shopBn": "${shopbn}$", 133 | "storeNo": "${storeNo}$", 134 | "storeName": "${storeName}$", 135 | "newStoreNo": "W031", 136 | "newStoreName": "四川成都旗舰店", 137 | "areaState": "四川省", 138 | "areaCity": "成都市", 139 | "areaDistrict": "武侯区", 140 | "addr": "环球中心", 141 | "applyTime": "2018-11-10", 142 | "reassignReason": "无货", 143 | "mark": null, 144 | "operatorName": "rjf" 145 | } 146 | } 147 | 148 | 149 | ] -------------------------------------------------------------------------------- /TestScene/test_ModifySend/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=11112798abcd 3 | shopbn=newkhronghe 4 | storeNo=J0S1 5 | storeName=江苏仓库店 6 | 7 | -------------------------------------------------------------------------------- /TestScene/test_ModifySend/test_ModifySend.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_OrderUnbinding/__init__.py -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_04_ModifySend # 用例ID, 用于识别 string 4 | title: 收单到订单号绑定 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 订单号绑定小号 # 必填,parameter为文件路径时 32 | info: 绑定订单号 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: raw # 参数类型 36 | address: /centerDocker/api/newOrderLogisticsPhone/bindPhone?shopBn=newkhronghe×tamp=20190716190000&sign=d82e03a1a2012f314904774909446de6&isRightNow=off 37 | headers: # 请求头 38 | Content-Type: application/json 39 | timeout: 20 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: success 47 | relevance: # 关联键 48 | 49 | - test_name: 订单号解绑小号 # 必填,parameter为文件路径时 50 | info: 订单状态接口反写 51 | http_type: http # 请求协议 52 | request_type: post # 请求类型 53 | parameter_type: raw # 参数类型 54 | address: /centerDocker/api/orderLogisticsPhone/unbind?shopBn=newkhronghe×tamp=20190716190000&sign=d82e03a1a2012f314904774909446de6&isRightNow=off 55 | headers: # 请求头 56 | Content-Type: application/json 57 | timeout: 20 58 | parameter: param.json 59 | file: false 60 | check: # 校验列表 list or dict 61 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 62 | datebase: 63 | expected_code: 200 64 | expected_request: success 65 | relevance: # 关联键 66 | 67 | -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "天猫", 6 | "to_node_id": "1488934732", 7 | "shopBn": "newkhronghe", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "1016865", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "1012217", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "19878637829", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "6558EAFB4CE824F4EC467DE4DE5EBBC3", 106 | "work_routine": null, 107 | "members_phone": "8B84CDDD502888900C984C1956016BE8", 108 | "storeName": null, 109 | "memberAccount": null, 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | }, 128 | { 129 | "test_name":"订单号绑定小号","parameter":{ 130 | "orderBn": "${order_bn}$", 131 | "phone": "18782961185", 132 | "callType":0, 133 | "appId":"pos", 134 | "token":"4e634b8a8e5e4a1316b60ca90cf51f81", 135 | "account":"1919003", 136 | "storeNo":"W031" 137 | } 138 | }, 139 | { 140 | "test_name":"订单号解绑小号","parameter":{ 141 | "orderBn": "${order_bn}$", 142 | "phone": "18782961185", 143 | "smallNumber": "17150144384", 144 | "logisticsType": null, 145 | "callType":null 146 | } 147 | 148 | } 149 | ] -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=111120021ss 3 | shopbn=newkhronghe 4 | deliveryBn=454545467787019a 5 | returnGoodsBn=559949411118 6 | -------------------------------------------------------------------------------- /TestScene/test_OrderUnbinding/test_OrderUnbinding.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_ReturnGoods/__init__.py -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_06_unbunding # 用例ID, 用于识别 string 4 | title: 收单到会员Id绑定 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 退货单信息处理接口 # 必填,parameter为文件路径时 32 | info: 退货单信息处理接口 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: raw # 参数类型 36 | address: /centerDocker/api/returnGoods/pushAndSaveReturnGoods?shopBn=mendianapp×tamp=20190716190000&sign=483b4f45c285d61d58596ae7fefbdb84 37 | headers: # 请求头 38 | Content-Type: application/json 39 | timeout: 20 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: 处理成功! 47 | relevance: # 关联键 48 | 49 | -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "天猫", 6 | "to_node_id": "1488934732", 7 | "shopBn": "${shopbn}$", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "1016865", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "1012217", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "test", 106 | "work_routine": null, 107 | "members_phone": null, 108 | "storeName": null, 109 | "memberAccount": "17761272084", 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | 128 | }, 129 | { 130 | "test_name": "退货单信息处理接口", "parameter": {"costShippig":0, 131 | "orderPmtOrder":0, 132 | "applyTime":1531913662000, 133 | "protect":false, 134 | "applyStatus":"2", 135 | "returnGoodsBn":"${returnGoodsBn}$", 136 | "storeNo":"W031", 137 | "createtime":1531843200000, 138 | "orderSource":"pos", 139 | "orderCostAmount":0.5, 140 | "storeAddress":"天府大道1700号", 141 | "orderStatus":"finish", 142 | "orderTotalAmount":0.5, 143 | "operatorName":"运维专用", 144 | "platformSource":"pos", 145 | "orderPmtPlateform":0, 146 | "shopBn":"mendianapp", 147 | "returnAmount":0.5, 148 | "orderBn":"${order_bn}$", 149 | "returnProductInfos":[{"unit":"EA", 150 | "productBn":"2000022", 151 | "specification":"1", 152 | "quantity":1, 153 | "orderBn":"${order_bn}$", 154 | "productName":"1919环保袋", 155 | "gPrice":120}], 156 | "costProtect":0,"mobile":"199C16C72C98BEF2B438CD355924BB8F"} 157 | } 158 | ] -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=1111224576d 3 | shopbn=newkhronghe 4 | returnGoodsBn=N1531913566783299K3 5 | 6 | 7 | -------------------------------------------------------------------------------- /TestScene/test_ReturnGoods/test_ReturnGoods.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_ZhongTaiCancel/__init__.py -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_06_unbunding # 用例ID, 用于识别 string 4 | title: 收单到会员Id绑定 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 中台取消接口 # 必填,parameter为文件路径时 32 | info: 中台取消接口 33 | host: 13.14.100.14:8098 34 | http_type: http # 请求协议 35 | request_type: get # 请求类型 36 | parameter_type: raw # 参数类型 37 | address: /omsAdmin/admin/cancel/cancelOrder.jhtml 38 | headers: {"Cookie": "JSESSIONID=9160E84F5BF85DD0D73FDE518868ED40"} 39 | timeout: 20 40 | parameter: param.json 41 | file: false 42 | check: # 校验列表 list or dict 43 | - check_type: no_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 44 | datebase: 45 | expected_code: 200 46 | expected_request: 47 | relevance: # 关联键 48 | 49 | -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "新快喝", 6 | "to_node_id": "1488934732", 7 | "shopBn": "newkhronghe", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "1016865", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "1012217", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "6558EAFB4CE824F4EC467DE4DE5EBBC3", 106 | "work_routine": null, 107 | "members_phone": "8B84CDDD502888900C984C1956016BE8", 108 | "storeName": null, 109 | "memberAccount": null, 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | }, 128 | { 129 | "test_name": "中台取消接口", "parameter": { 130 | "orderBn": "${order_bn}$", 131 | "cancelMask":"sdfsdf", 132 | "cancelComment": "sdfsdfsdf" 133 | } 134 | 135 | } 136 | ] -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=111122abddddd 3 | shopbn=newkhronghe 4 | 5 | 6 | -------------------------------------------------------------------------------- /TestScene/test_ZhongTaiCancel/test_ZhongTaiCancel.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_addProject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_addProject/__init__.py -------------------------------------------------------------------------------- /TestScene/test_addProject/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_addProject # 用例ID, 用于识别 string 4 | title: 添加项目流程 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_platform}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: /api/project/add_project # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | # 测试用例 11 | test_case: 12 | - test_name: 登陆1 # 必填 parameter为文件路径时 string 13 | info: 正常登陆1 # 选填 string 14 | host: ${test_platform}$ # 基本信息中若填写,此处为选填 string 15 | address: /api/user/login # 请求接口 string 16 | http_type: http # 请求协议 string 17 | request_type: post # 请求方式 string 18 | parameter_type: form-data # 参数类型 string 19 | headers: {} # 请求头 dict 20 | timeout: 8 # 超时时间 int 21 | parameter: # 可填实际传递参数,若参数过多,可保存在相应的参数文件中,用test_name作为索引 string or dict 22 | username: litao 23 | password: lt19910301 24 | file: false # 是否上传文件,默认false,若上传文件接口,此处为文件相对路径 bool or string 25 | check: # 校验列表 list or dict 26 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 27 | datebase: 28 | expected_code: 200 29 | expected_request: 王八蛋 30 | CustomFail: # 自定义失败说明 31 | relevance: # 关联的键 list or string 32 | - 王八蛋 33 | # 第一条case,info可不填 34 | - test_name: 添加项目 # 必填,parameter为文件路径时 35 | info: 添加项目 36 | http_type: http # 请求协议 37 | request_type: post # 请求类型 38 | parameter_type: raw # 参数类型 39 | headers: # 请求头 40 | Authorization: Token ${key}$ 41 | Content-Type: application/json 42 | timeout: 8 43 | parameter: 44 | name: ${name}$ 45 | type: ${type}$ 46 | version: ${version}$ 47 | description: ${description}$ 48 | file: false 49 | check: # 校验列表 list or dict 50 | - check_type: no_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 51 | datebase: 52 | expected_code: 53 | expected_request: 54 | CustomFail: # 自定义失败说明 55 | relevance: # 关联键 56 | -------------------------------------------------------------------------------- /TestScene/test_addProject/expected.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 4 | }, 5 | { 6 | "test_name": "登陆4", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 7 | }, 8 | { 9 | "test_name": "登陆2", "json": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 10 | } 11 | ] -------------------------------------------------------------------------------- /TestScene/test_addProject/logs/2018-11-13.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_addProject/logs/2018-11-13.log -------------------------------------------------------------------------------- /TestScene/test_addProject/logs/2018-11-13_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_addProject/logs/2018-11-13_error.log -------------------------------------------------------------------------------- /TestScene/test_addProject/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name": "登陆3", "parameter": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 4 | }, 5 | { 6 | "test_name": "登陆4", "parameter": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 7 | }, 8 | { 9 | "test_name": "登陆2", "parameter": {"code":"999999","msg":"成功","data":{"first_name":"Tom","last_name":"","phone":"123456789","email":"123@qq.com","key":"e1dfbfd759ad46bcdc44df989ade3f1c190cc936","date_joined":"2018-06-28 02:54:00","userphoto":"/file/userphoto.jpg"}} 10 | } 11 | ] -------------------------------------------------------------------------------- /TestScene/test_addProject/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | name=$RandomInt(0,10)$ 3 | type=Web 4 | version=$RandomString(10)$ 5 | description=$RandomString(10)$ -------------------------------------------------------------------------------- /TestScene/test_addProject/test_AddProjectscene.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /TestScene/test_cancel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/TestScene/test_cancel/__init__.py -------------------------------------------------------------------------------- /TestScene/test_cancel/case.yaml: -------------------------------------------------------------------------------- 1 | # 用例基本信息 2 | testinfo: 3 | id: test_04_ModifySend # 用例ID, 用于识别 string 4 | title: 收单到取消场景用例 # 用例标题,在报告中作为一级目录显示 必填 string 5 | host: ${test_refund}$ # 请求的域名,可写死,也可写成模板关联host配置文件 选填(此处不填,每条用例必填) string 6 | address: # 请求地址 选填(此处不填,每条用例必填) string 7 | 8 | # 前置条件,case之前需关联的接口 9 | premise: 10 | 11 | # 测试用例 12 | test_case: 13 | # 第一条case,info可不填 14 | - test_name: 订单收单接口 # 必填,parameter为文件路径时 15 | http_type: http # 请求协议 16 | request_type: post # 请求类型 17 | parameter_type: raw # 参数类型 18 | address: /centerDocker/api/pushOrder/order?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 19 | headers: # 请求头 20 | Content-Type: application/json 21 | timeout: 20 22 | parameter: param.json 23 | file: false 24 | check: # 校验列表 list or dict 25 | - check_type: json # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 26 | datebase: 27 | expected_code: 200 28 | expected_request: {} 29 | relevance: # 关联键 30 | 31 | - test_name: 订单取消接口 # 必填,parameter为文件路径时 32 | info: 订单取消接口 33 | http_type: http # 请求协议 34 | request_type: post # 请求类型 35 | parameter_type: form-data # 参数类型 36 | address: /centerDocker/order/aftersale/cancel?shopBn=tianmao×tamp=20190716190000&sign=22d843399f02bc3ab64c24eaaab024d8&isRightNow=true 37 | headers: {} # 请求头 38 | timeout: 20 39 | parameter: param.json 40 | file: false 41 | check: # 校验列表 list or dict 42 | - check_type: Regular_check # 校验类型 string 不校验时 datebase, expected_code, expected_request 均可不填 43 | datebase: 44 | expected_code: 200 45 | expected_request: 取消成功! 46 | relevance: # 关联键 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /TestScene/test_cancel/expected.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /TestScene/test_cancel/param.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "test_name":"订单收单接口","parameter":{ 4 | "id": null, 5 | "ordersource": "新快喝", 6 | "to_node_id": "1488934732", 7 | "shopBn": "${shopbn}$", 8 | "orderType": "normal", 9 | "order_bn": "${order_bn}$", 10 | "cost_item": 1256.00, 11 | "cost_tax": null, 12 | "createtime": "2018-09-10", 13 | "cur_amount": 789.00, 14 | "currency": "CNY", 15 | "custom_mark": null, 16 | "discount": null, 17 | "is_tax": false, 18 | "invoiceType": "普通发票", 19 | "invoiceTitle": null, 20 | "invoiceDetails": null, 21 | "isPaperInvoice": false, 22 | "lastmodify": "2018-09-10", 23 | "modified": null, 24 | "order_items": [{ 25 | "bn": "2000021", 26 | "name": "【欢聚价】【极速达】1919酒类直供43度飞天茅台 贵州 国产酱香型白酒500ml", 27 | "quantity": 1, 28 | "price": 1198.00, 29 | "amount": 1198.00, 30 | "weight": null, 31 | "g_price": 978.00, 32 | "productType": "商品", 33 | "isOwnBn": false, 34 | "splitProducts": [], 35 | "productRebateAmount": null, 36 | "rechargeRebateAmount": null 37 | }, { 38 | "bn": "2000022", 39 | "name": "1919酒类直供 古南丰金六年花雕酒 500mL 黄酒 糯米酒", 40 | "quantity": 1, 41 | "price": 58.00, 42 | "amount": 58.00, 43 | "weight": null, 44 | "g_price": 23.00, 45 | "productType": "商品", 46 | "isOwnBn": false, 47 | "splitProducts": [], 48 | "productRebateAmount": null, 49 | "rechargeRebateAmount": null 50 | }], 51 | "pay_bn": "支付宝", 52 | "pay_status": 1, 53 | "payed": 789.00, 54 | "payinfo": { 55 | "cost_payment": 0, 56 | "pay_app_id": "支付宝", 57 | "pay_app_bn": "03", 58 | "payAcountInfo": null, 59 | "paymentAmount": 789.00 60 | }, 61 | "pmt_detail": [{ 62 | "pmt_amount": 219.63, 63 | "pmt_memo": "店铺优惠 :219.63" 64 | }, { 65 | "pmt_amount": 4.37, 66 | "pmt_memo": "店铺优惠 :4.37" 67 | }], 68 | "pmt_order": 224.00, 69 | "pmt_goods": 255.00, 70 | "pmt_plateform": null, 71 | "ship_status": 0, 72 | "shipping": { 73 | "orderId": null, 74 | "orderBn": null, 75 | "shipping_name": "门店自配送", 76 | "is_cod": false, 77 | "is_protect": false, 78 | "cost_shipping": 12.00, 79 | "cost_protect": 0, 80 | "sendTimeContent": "" 81 | }, 82 | "status": "active", 83 | "total_amount": 789.00, 84 | "consignee": { 85 | "orderId": null, 86 | "orderBn": null, 87 | "email": null, 88 | "name": "彭先生", 89 | "zip": "000000", 90 | "area_state": "江苏省", 91 | "area_city": "南京市", 92 | "area_district": "雨花台区", 93 | "telephone": null, 94 | "mobile": "8B84CDDD502888900C984C1956016BE8", 95 | "addr": "赛虹桥街道江苏省南京市雨花台区小行路20号消防中队家属院2栋3单元", 96 | "longitude": null, 97 | "latitude": null 98 | }, 99 | "storeNo": null, 100 | "isPumpSplit": null, 101 | "shippingOrder": 2, 102 | "isHasDelivery": null, 103 | "deliveryVersion": null, 104 | "channelsOrder": null, 105 | "members_name": "6558EAFB4CE824F4EC467DE4DE5EBBC3", 106 | "work_routine": null, 107 | "members_phone": "8B84CDDD502888900C984C1956016BE8", 108 | "storeName": null, 109 | "memberAccount": null, 110 | "memberLevel": 0, 111 | "sellerMark": null, 112 | "sendingTime": null, 113 | "lastModifyDeliveryDate": null, 114 | "taxAmount": null, 115 | "incomeStoreNo": null, 116 | "operatorName": null, 117 | "taxId": null, 118 | "registPhoneNo": null, 119 | "registAddr": null, 120 | "registBank": null, 121 | "registAcct": null, 122 | "sellerCode": null, 123 | "sellerName": null, 124 | "currentVersion": "currentversion_null", 125 | "getWalletSuccess": null 126 | } 127 | }, 128 | { 129 | "test_name":"订单取消接口","parameter":{ 130 | "orderBn": "${order_bn}$", 131 | "cancelMask":"skwod" 132 | } 133 | } 134 | 135 | 136 | ] -------------------------------------------------------------------------------- /TestScene/test_cancel/relevance.ini: -------------------------------------------------------------------------------- 1 | [relevance] 2 | order_bn=111127ddd 3 | shopbn=newkhronghe 4 | storeNo=J0S1 5 | storeName=江苏仓库店 6 | 7 | -------------------------------------------------------------------------------- /TestScene/test_cancel/test_cancel.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | 4 | import allure 5 | import pytest 6 | 7 | from Common import init 8 | 9 | # 读取测试用例 10 | from Common.IniCase import ini_case 11 | from Common.IniRelevance import ini_relevance 12 | from Common.TestAndCheck import api_send_check 13 | from config.ConfigLogs import LogConfig 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | case_dict = ini_case(PATH) 19 | # relevance = ini_relevance(PATH) 20 | 21 | 22 | @allure.feature(case_dict["testinfo"]["title"]) # feature定义功能 23 | class TestAddProject: 24 | 25 | @classmethod 26 | def setup_class(cls): 27 | cls.rel = ini_relevance(PATH) 28 | # 设置一个类变量记录用例测试结果,场景测试时,流程中的一环校验错误,则令result的值为False, 29 | cls.result = {"result": True} 30 | cls.relevance = cls.rel.copy() 31 | cls.relevance = init.ini_request(case_dict, cls.relevance, PATH, cls.result) 32 | 33 | def setup(self): 34 | pass 35 | 36 | # @pytest.mark.skipif(sys.version_info < (3, 6)) # 跳过条件 37 | @pytest.mark.parametrize("case_data", case_dict["test_case"]) 38 | @allure.story("添加项目") 39 | @allure.issue("http://www.baidu.com") # bug地址 40 | @allure.testcase("http://www.testlink.com") # 用例连接地址 41 | @pytest.mark.flaky(reruns=3, reruns_delay=3) 42 | def test_add_project(self, case_data): 43 | """ 44 | 添加项目测试 # 第一条用例描述 45 | :param case_data: 参数化用例的形参 46 | :return: 47 | """ 48 | # 参数化修改test_add_project 注释 49 | for k, v in enumerate(case_dict["test_case"]): # 遍历用例文件中所有用例的索引和值 50 | try: 51 | if case_data == v: 52 | # 修改方法的__doc__在下一次调用时生效,此为展示在报告中的用例描述 53 | TestAddProject.test_add_project.__doc__ = case_dict["test_case"][k+1]["info"] 54 | except IndexError: 55 | pass 56 | if not self.result["result"]: 57 | # 查看类变量result的值,如果未False,则前一接口校验错误,此接口标记未失败,节约测试时间 58 | pytest.xfail("前置接口测试失败,此接口标记为失败") 59 | # 发送测试请求 60 | api_send_check(case_data, case_dict, self.relevance, self.rel, PATH, self.result) 61 | 62 | 63 | if __name__ == "__main__": 64 | LogConfig(PATH) 65 | pytest.main() 66 | -------------------------------------------------------------------------------- /config/ConfRelevance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 20:08 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: ConfRelevance.py 10 | 11 | # @Software: PyCharm 12 | 13 | import configparser 14 | import logging 15 | 16 | from Common.FunctionReplace import function_replace 17 | 18 | 19 | class ConfRelevance: 20 | # 关联文件读取配置 21 | def __init__(self, _path): 22 | logging.info("初始化关联文件") 23 | config = configparser.ConfigParser() 24 | config.read(_path, encoding="utf-8") 25 | self.host = config["relevance"] 26 | 27 | def get_relevance_conf(self): 28 | relevance = dict() 29 | logging.debug("读取初始关联文件内容: %s" % self.host.items()) 30 | for key, value in self.host.items(): 31 | relevance[key] = function_replace(value) 32 | logging.debug("初始关联内容数据处理后: %s" % relevance) 33 | return relevance 34 | 35 | 36 | if __name__ == "__main__": 37 | host = ConfRelevance("path") 38 | -------------------------------------------------------------------------------- /config/ConfigLogs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/12 10:26 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: ConfigLogs.py 10 | 11 | # @Software: PyCharm 12 | import logging 13 | import time 14 | 15 | from Common.MkDir import mk_dir 16 | 17 | 18 | class LogConfig: 19 | def __init__(self, path): 20 | """ 21 | 日志配置 22 | :param path: case 路径 23 | """ 24 | runtime = time.strftime('%Y-%m-%d', time.localtime(time.time())) 25 | logger = logging.getLogger() 26 | logger.setLevel(logging.DEBUG) # Log等级总开关 27 | # 第二步,创建一个handler,用于写入全部日志文件 28 | mk_dir(path+"\logs\\") 29 | logfile = path + "\logs\\" + runtime + '.log' 30 | fh = logging.FileHandler(logfile, mode='w+') 31 | fh.setLevel(logging.DEBUG) # 输出到file的log等级的开关 32 | # 第三步,创建一个handler,用于写入错误日志文件 33 | logfile_error = path + "\logs\\" + runtime + '_error.log' 34 | fh_error = logging.FileHandler(logfile_error, mode='w+') 35 | fh_error.setLevel(logging.ERROR) # 输出到file的log等级的开关 36 | # 第四步,再创建一个handler,用于输出到控制台 37 | ch = logging.StreamHandler() 38 | ch.setLevel(logging.INFO) # 输出到console的log等级的开关 39 | # 第五步,定义handler的输出格式 40 | formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") 41 | fh.setFormatter(formatter) 42 | fh_error.setFormatter(formatter) 43 | ch.setFormatter(formatter) 44 | # 第六步,将logger添加到handler里面 45 | logger.addHandler(fh) 46 | logger.addHandler(fh_error) 47 | logger.addHandler(ch) 48 | logging.basicConfig(level=logging.DEBUG, 49 | format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', 50 | datefmt='%a, %d %b %Y %H:%M:%S', 51 | filename=logfile, 52 | filemode='w') 53 | logging.basicConfig(level=logging.ERROR, 54 | format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', 55 | datefmt='%a, %d %b %Y %H:%M:%S', 56 | filename=fh_error, 57 | filemode='w') 58 | 59 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/config/__init__.py -------------------------------------------------------------------------------- /config/configHost.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import configparser 13 | import os 14 | 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | 17 | 18 | class ConfHost: 19 | # host文件读取配置 20 | def __init__(self): 21 | config = configparser.ConfigParser() 22 | config.read(PATH+"/host.ini", encoding="utf-8") 23 | self.host = config["host"] 24 | 25 | def get_host_conf(self): 26 | return self.host 27 | 28 | 29 | if __name__ == "__main__": 30 | host = ConfHost() 31 | for k, v in host.get_host_conf(): 32 | print(k, v) 33 | 34 | -------------------------------------------------------------------------------- /config/host.ini: -------------------------------------------------------------------------------- 1 | [host] 2 | test_platform=120.79.232.23 3 | mock=10.4.100.209 4 | test_refund=13.14.100.14:18181 5 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # @Time    : 2018/11/9 15:06 4 | 5 | # @Author  : litao 6 | 7 | # @Project : project 8 | 9 | # @FileName: GetRelevance.py 10 | 11 | # @Software: PyCharm 12 | import random 13 | 14 | import pytest 15 | import allure 16 | import requests 17 | 18 | from config.configHost import ConfHost 19 | 20 | 21 | @pytest.fixture(scope="session", autouse=True) 22 | def env(request): 23 | """ 24 | Parse env config info 25 | """ 26 | host_config = ConfHost() 27 | host = host_config.get_host_conf() 28 | allure.environment(test_platform=host["test_platform"]) 29 | allure.environment(mock=host["mock"]) 30 | 31 | 32 | @pytest.fixture(scope="session", autouse=True) 33 | def login(request): 34 | # setup 35 | url = "http://120.79.232.23/api/user/login" 36 | parameter = {"username": "litao", "password": "lt19910301"} 37 | response = requests.post(url=url, data=parameter) 38 | yield 39 | # teardown 40 | print("测试完成") 41 | print(response.json()) 42 | -------------------------------------------------------------------------------- /data/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/data/cover.jpg -------------------------------------------------------------------------------- /logs/2018-11-12.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-12.log -------------------------------------------------------------------------------- /logs/2018-11-12_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-12_error.log -------------------------------------------------------------------------------- /logs/2018-11-13.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-13.log -------------------------------------------------------------------------------- /logs/2018-11-13_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-13_error.log -------------------------------------------------------------------------------- /logs/2018-11-15.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-15.log -------------------------------------------------------------------------------- /logs/2018-11-15_error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/logs/2018-11-15_error.log -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | Created on 2017年5月5日 4 | 5 | @author: lt 6 | """ 7 | # coding:utf-8 8 | import os 9 | 10 | import pytest 11 | 12 | from config.ConfigLogs import LogConfig 13 | 14 | case_path = os.path.join(os.getcwd()) 15 | PATH = os.path.split(os.path.realpath(__file__))[0] 16 | failureException = AssertionError 17 | 18 | if __name__ == '__main__': 19 | LogConfig(PATH) 20 | pytest.main("%s --alluredir report" % case_path) 21 | # pytest.main() 22 | os.popen("allure generate report/ -o result/ --clean") 23 | os.popen("allure open -h 0.0.0.0 -p 8083 result/") 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | simplejson==3.16.0 2 | requests==2.20.0 3 | pytest==3.9.3 4 | pytest_allure_adaptor==1.7.10 5 | PyYAML==3.13 6 | configparser==3.5.0 7 | pytest-rerunfailures==5.0 -------------------------------------------------------------------------------- /window运行.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/githublitao/PyTest_allure_apitest/b4a7f8daef61123c0cf051bf6895edb0669bb1e0/window运行.bat --------------------------------------------------------------------------------