├── .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
--------------------------------------------------------------------------------