api-engine-xin 0.0.22__tar.gz → 0.0.25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api_engine_xin-0.0.25/ApiEngine/BaseCase.py +2 -0
- api_engine_xin-0.0.25/ApiEngine/__init__.py +3 -0
- api_engine_xin-0.0.25/ApiEngine/core.py +407 -0
- api_engine_xin-0.0.25/ApiEngine/engine/__init__.py +1 -0
- api_engine_xin-0.0.25/ApiEngine/engine/assertion.py +108 -0
- api_engine_xin-0.0.25/ApiEngine/engine/base_case.py +339 -0
- api_engine_xin-0.0.25/ApiEngine/engine/extractor.py +46 -0
- api_engine_xin-0.0.25/ApiEngine/engine/precondition.py +239 -0
- api_engine_xin-0.0.25/ApiEngine/engine/replacer.py +113 -0
- api_engine_xin-0.0.25/ApiEngine/engine/script_runner.py +34 -0
- api_engine_xin-0.0.25/ApiEngine/http/__init__.py +1 -0
- api_engine_xin-0.0.25/ApiEngine/http/client.py +111 -0
- api_engine_xin-0.0.25/ApiEngine/http/files.py +60 -0
- api_engine_xin-0.0.25/ApiEngine/infra/__init__.py +3 -0
- api_engine_xin-0.0.25/ApiEngine/infra/case_log.py +52 -0
- api_engine_xin-0.0.25/ApiEngine/infra/db_client.py +99 -0
- api_engine_xin-0.0.25/ApiEngine/infra/exceptions.py +5 -0
- api_engine_xin-0.0.25/ApiEngine/infra/global_func.py +2 -0
- api_engine_xin-0.0.22/ApiEngine/testResult.py → api_engine_xin-0.0.25/ApiEngine/infra/test_result.py +5 -5
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/LICENSE +1 -1
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/PKG-INFO +5 -1
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/api_engine_xin.egg-info/PKG-INFO +5 -1
- api_engine_xin-0.0.25/api_engine_xin.egg-info/SOURCES.txt +30 -0
- api_engine_xin-0.0.25/api_engine_xin.egg-info/requires.txt +17 -0
- api_engine_xin-0.0.25/setup.py +45 -0
- api_engine_xin-0.0.25/tests/runTest.py +326 -0
- api_engine_xin-0.0.22/ApiEngine/BaseCase.py +0 -780
- api_engine_xin-0.0.22/ApiEngine/__init__.py +0 -3
- api_engine_xin-0.0.22/ApiEngine/caseLog.py +0 -52
- api_engine_xin-0.0.22/ApiEngine/core.py +0 -454
- api_engine_xin-0.0.22/ApiEngine/dbClient.py +0 -103
- api_engine_xin-0.0.22/ApiEngine/global_func.py +0 -0
- api_engine_xin-0.0.22/api_engine_xin.egg-info/SOURCES.txt +0 -18
- api_engine_xin-0.0.22/api_engine_xin.egg-info/requires.txt +0 -2
- api_engine_xin-0.0.22/setup.py +0 -38
- api_engine_xin-0.0.22/tests/runTest.py +0 -58
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/README.md +0 -0
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/api_engine_xin.egg-info/dependency_links.txt +0 -0
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/api_engine_xin.egg-info/top_level.txt +0 -0
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/setup.cfg +0 -0
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/tests/Tools.py +0 -0
- {api_engine_xin-0.0.22 → api_engine_xin-0.0.25}/tests/__init__.py +0 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
from ApiEngine import log
|
|
4
|
+
from ApiEngine.infra import global_func
|
|
5
|
+
from ApiEngine.infra.exceptions import PreconditionChainError
|
|
6
|
+
from ApiEngine.infra.test_result import TestResult
|
|
7
|
+
from ApiEngine.infra.db_client import DBClient
|
|
8
|
+
from ApiEngine.engine.base_case import BaseCase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestRunner:
|
|
12
|
+
def __init__(self, env_data):
|
|
13
|
+
"""
|
|
14
|
+
:param env_data: 执行测试时的环境数据
|
|
15
|
+
"""
|
|
16
|
+
# 深拷贝环境数据,避免修改调用方原始数据
|
|
17
|
+
self.env_data = copy.deepcopy(env_data)
|
|
18
|
+
# debug_updates 保持原引用,变更对上层可见
|
|
19
|
+
if "debug_updates" in env_data:
|
|
20
|
+
self.env_data["debug_updates"] = env_data["debug_updates"]
|
|
21
|
+
self.result = []
|
|
22
|
+
self._db = DBClient()
|
|
23
|
+
self._shared_env = {} # 实例级共享环境
|
|
24
|
+
|
|
25
|
+
def execute_cases(self, testcases):
|
|
26
|
+
"""执行测试用例的方法"""
|
|
27
|
+
# 根据数据库的配置初始化数据库的连接
|
|
28
|
+
db_config = self.env_data.pop("db", None)
|
|
29
|
+
if db_config:
|
|
30
|
+
self._db.init_connect(db_config)
|
|
31
|
+
|
|
32
|
+
# 统一初始化共享环境
|
|
33
|
+
self._shared_env.clear()
|
|
34
|
+
self._shared_env.update(self.env_data)
|
|
35
|
+
self._load_global_func()
|
|
36
|
+
|
|
37
|
+
# 判断测试数据参数的类型
|
|
38
|
+
if isinstance(testcases, dict):
|
|
39
|
+
cases = testcases.get("cases")
|
|
40
|
+
if cases:
|
|
41
|
+
log.info_log("执行测试套件:", testcases["name"])
|
|
42
|
+
test_result = TestResult(all=len(testcases["cases"]), name=testcases["name"])
|
|
43
|
+
for case in testcases["cases"]:
|
|
44
|
+
log.info_log(case)
|
|
45
|
+
self.perform_case(case, test_result)
|
|
46
|
+
res = test_result.get_result_info()
|
|
47
|
+
self.result.append(res)
|
|
48
|
+
else:
|
|
49
|
+
log.info_log("调试单条接口用例:", testcases["title"])
|
|
50
|
+
test_result = TestResult(all=1)
|
|
51
|
+
self.perform_case(testcases, test_result)
|
|
52
|
+
res = test_result.get_result_info()
|
|
53
|
+
self.result = res["cases"][0]
|
|
54
|
+
elif isinstance(testcases, list):
|
|
55
|
+
results = []
|
|
56
|
+
for items in testcases:
|
|
57
|
+
log.info_log("执行测试套件:", items["name"])
|
|
58
|
+
test_result = TestResult(all=len(items["cases"]), name=items["name"])
|
|
59
|
+
for case in items["cases"]:
|
|
60
|
+
self.perform_case(case, test_result)
|
|
61
|
+
res = test_result.get_result_info()
|
|
62
|
+
results.append(res)
|
|
63
|
+
total_all = 0
|
|
64
|
+
total_success = 0
|
|
65
|
+
total_fail = 0
|
|
66
|
+
total_error = 0
|
|
67
|
+
for scence_result in results:
|
|
68
|
+
total_all += scence_result["all"]
|
|
69
|
+
total_success += scence_result["success"]
|
|
70
|
+
total_fail += scence_result["fail"]
|
|
71
|
+
total_error += scence_result["error"]
|
|
72
|
+
self.result = {
|
|
73
|
+
"results": results,
|
|
74
|
+
"all": total_all,
|
|
75
|
+
"success": total_success,
|
|
76
|
+
"fail": total_fail,
|
|
77
|
+
"error": total_error
|
|
78
|
+
}
|
|
79
|
+
else:
|
|
80
|
+
log.error_log("测试数据格式错误")
|
|
81
|
+
|
|
82
|
+
# 断开数据库连接
|
|
83
|
+
if db_config:
|
|
84
|
+
self._db.close_db_connect()
|
|
85
|
+
return self.result
|
|
86
|
+
|
|
87
|
+
def perform_case(self, case, test_result):
|
|
88
|
+
"""执行单条用例"""
|
|
89
|
+
# 每次创建新的 BaseCase 实例,传入共享环境引用
|
|
90
|
+
c = BaseCase(shared_env=self._shared_env)
|
|
91
|
+
c._db = self._db
|
|
92
|
+
try:
|
|
93
|
+
c.perform(case)
|
|
94
|
+
except PreconditionChainError:
|
|
95
|
+
test_result.add_fail(c)
|
|
96
|
+
except AssertionError:
|
|
97
|
+
test_result.add_fail(c)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
test_result.add_error(c, e)
|
|
100
|
+
else:
|
|
101
|
+
test_result.add_success(c)
|
|
102
|
+
|
|
103
|
+
def _load_global_func(self):
|
|
104
|
+
"""安全加载用户自定义函数,避免跨执行污染"""
|
|
105
|
+
_gf = self.env_data.get("global_func")
|
|
106
|
+
if not (_gf and isinstance(_gf, str)):
|
|
107
|
+
return
|
|
108
|
+
# 只保留 __name__ 等双下划线内置属性
|
|
109
|
+
builtins = {k: v for k, v in global_func.__dict__.items() if k.startswith('__')}
|
|
110
|
+
global_func.__dict__.clear()
|
|
111
|
+
global_func.__dict__.update(builtins)
|
|
112
|
+
exec(_gf, global_func.__dict__)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == '__main__':
|
|
116
|
+
import os
|
|
117
|
+
|
|
118
|
+
# 获取测试辅助文件的绝对路径
|
|
119
|
+
_test_dir = os.path.join(os.path.dirname(__file__), '..', 'tests')
|
|
120
|
+
_setup = open(os.path.join(_test_dir, 'setup_scripts.txt'), 'r', encoding='utf-8').read()
|
|
121
|
+
_teardown = open(os.path.join(_test_dir, 'teardown_scripts.txt'), 'r', encoding='utf-8').read()
|
|
122
|
+
_tools = open(os.path.join(_test_dir, 'Tools.py'), 'r', encoding='utf-8').read()
|
|
123
|
+
|
|
124
|
+
test_case = {
|
|
125
|
+
"title": "登录成功",
|
|
126
|
+
"interface": {
|
|
127
|
+
"url": "/member/public/login",
|
|
128
|
+
"method": "post"
|
|
129
|
+
},
|
|
130
|
+
"headers": {
|
|
131
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
132
|
+
},
|
|
133
|
+
"request": {
|
|
134
|
+
"data": {
|
|
135
|
+
"keywords": "13012341231",
|
|
136
|
+
"password": "test123"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"setup_script": _setup,
|
|
140
|
+
"teardown_script": "",
|
|
141
|
+
"extract": [
|
|
142
|
+
{"var_name": "status", "extract_expr": "$.status"},
|
|
143
|
+
{"var_name": "description", "extract_expr": "$.description"}
|
|
144
|
+
],
|
|
145
|
+
"assertions": [
|
|
146
|
+
{"type": "相等", "field": "$.status", "expected": 200},
|
|
147
|
+
{"type": "相等", "field": "$.description", "expected": "登录成功"}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
test_case2 = {
|
|
152
|
+
"title": "验证是否登录成功",
|
|
153
|
+
"interface": {
|
|
154
|
+
"url": "/member/public/islogin",
|
|
155
|
+
"method": "post"
|
|
156
|
+
},
|
|
157
|
+
"headers": {},
|
|
158
|
+
"request": {
|
|
159
|
+
"params": {
|
|
160
|
+
"m": "Home",
|
|
161
|
+
"c": "User",
|
|
162
|
+
"a": "do_login",
|
|
163
|
+
"t": "${status}"
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
"preconditions": [
|
|
167
|
+
{
|
|
168
|
+
"title": "P2P登录成功",
|
|
169
|
+
"interface": {
|
|
170
|
+
"url": "/member/public/login",
|
|
171
|
+
"method": "post"
|
|
172
|
+
},
|
|
173
|
+
"headers": {
|
|
174
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
175
|
+
},
|
|
176
|
+
"request": {
|
|
177
|
+
"data": {
|
|
178
|
+
"keywords": "13012341231",
|
|
179
|
+
"password": "test123",
|
|
180
|
+
"description": "${description3}"
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"setup_script": _setup,
|
|
184
|
+
"teardown_script": "",
|
|
185
|
+
"preconditions": [
|
|
186
|
+
{
|
|
187
|
+
"title": "验证当前用户没有登录",
|
|
188
|
+
"interface": {
|
|
189
|
+
"url": "/member/public/islogin",
|
|
190
|
+
"method": "post"
|
|
191
|
+
},
|
|
192
|
+
"headers": {},
|
|
193
|
+
"request": {},
|
|
194
|
+
"setup_script": _setup,
|
|
195
|
+
"teardown_script": "",
|
|
196
|
+
"extract": [
|
|
197
|
+
{"var_name": "status3", "extract_expr": "$.status"},
|
|
198
|
+
{"var_name": "description3", "extract_expr": "$.description"}
|
|
199
|
+
],
|
|
200
|
+
"assertions": [
|
|
201
|
+
{"type": "相等", "field": "$.status", "expected": 200},
|
|
202
|
+
{"type": "相等", "field": "$.description", "expected": "您未登陆!"}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"extract": [
|
|
207
|
+
{"var_name": "status", "extract_expr": "$.status"},
|
|
208
|
+
{"var_name": "description", "extract_expr": "$.description"}
|
|
209
|
+
],
|
|
210
|
+
"assertions": [
|
|
211
|
+
{"type": "相等", "field": "$.status", "expected": 200},
|
|
212
|
+
{"type": "相等", "field": "$.description", "expected": "登录成功"}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"setup_script": _setup,
|
|
217
|
+
"teardown_script": "",
|
|
218
|
+
"extract": [
|
|
219
|
+
{"var_name": "status2", "extract_expr": "$.status"},
|
|
220
|
+
{"var_name": "description2", "extract_expr": "$.description"}
|
|
221
|
+
],
|
|
222
|
+
"assertions": [
|
|
223
|
+
{"type": "相等", "field": "$.status", "expected": "${status3}"},
|
|
224
|
+
{"type": "相等", "field": "$.description", "expected": "OK"}
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
test_suite = {
|
|
229
|
+
"name": "测试套件1",
|
|
230
|
+
"cases": [
|
|
231
|
+
{
|
|
232
|
+
"title": "登录接口",
|
|
233
|
+
"interface": {
|
|
234
|
+
"url": "/member/public/login",
|
|
235
|
+
"method": "post"
|
|
236
|
+
},
|
|
237
|
+
"headers": {
|
|
238
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
239
|
+
"Token": "${token}"
|
|
240
|
+
},
|
|
241
|
+
"request": {
|
|
242
|
+
"params": {},
|
|
243
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
244
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
245
|
+
"mobile": "${mobile}"},
|
|
246
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
247
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
248
|
+
"mobile": "${mobile}"}
|
|
249
|
+
},
|
|
250
|
+
"setup_script": _setup,
|
|
251
|
+
"teardown_script": _teardown
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"title": "登录接口2",
|
|
255
|
+
"interface": {
|
|
256
|
+
"url": "/member/public/login",
|
|
257
|
+
"method": "post"
|
|
258
|
+
},
|
|
259
|
+
"headers": {
|
|
260
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
261
|
+
"Token": "${token}"
|
|
262
|
+
},
|
|
263
|
+
"request": {
|
|
264
|
+
"params": {},
|
|
265
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
266
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
267
|
+
"mobile": "${mobile}"},
|
|
268
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
269
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
270
|
+
"mobile": "${mobile}"}
|
|
271
|
+
},
|
|
272
|
+
"setup_script": _setup,
|
|
273
|
+
"teardown_script": ""
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
test_suites = [
|
|
279
|
+
{
|
|
280
|
+
"name": "测试套件1",
|
|
281
|
+
"cases": [
|
|
282
|
+
{
|
|
283
|
+
"title": "登录接口",
|
|
284
|
+
"interface": {
|
|
285
|
+
"url": "/member/public/login",
|
|
286
|
+
"method": "post"
|
|
287
|
+
},
|
|
288
|
+
"headers": {
|
|
289
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
290
|
+
"Token": "${token}"
|
|
291
|
+
},
|
|
292
|
+
"request": {
|
|
293
|
+
"params": {},
|
|
294
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
295
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
296
|
+
"mobile": "${mobile}"},
|
|
297
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
298
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
299
|
+
"mobile": "${mobile}"}
|
|
300
|
+
},
|
|
301
|
+
"setup_script": _setup,
|
|
302
|
+
"teardown_script": _teardown
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"title": "登录接口2",
|
|
306
|
+
"interface": {
|
|
307
|
+
"url": "/member/public/login",
|
|
308
|
+
"method": "post"
|
|
309
|
+
},
|
|
310
|
+
"headers": {
|
|
311
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
312
|
+
"Token": "${token}"
|
|
313
|
+
},
|
|
314
|
+
"request": {
|
|
315
|
+
"params": {},
|
|
316
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
317
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
318
|
+
"mobile": "${mobile}"},
|
|
319
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
320
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
321
|
+
"mobile": "${mobile}"}
|
|
322
|
+
},
|
|
323
|
+
"setup_script": _setup,
|
|
324
|
+
"teardown_script": ""
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "测试套件2",
|
|
330
|
+
"cases": [
|
|
331
|
+
{
|
|
332
|
+
"title": "登录接口3",
|
|
333
|
+
"interface": {
|
|
334
|
+
"url": "/member/public/login",
|
|
335
|
+
"method": "post"
|
|
336
|
+
},
|
|
337
|
+
"headers": {
|
|
338
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
339
|
+
"Token": "${token}"
|
|
340
|
+
},
|
|
341
|
+
"request": {
|
|
342
|
+
"params": {},
|
|
343
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
344
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
345
|
+
"mobile": "${mobile}"},
|
|
346
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
347
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
348
|
+
"mobile": "${mobile}"}
|
|
349
|
+
},
|
|
350
|
+
"setup_script": _setup,
|
|
351
|
+
"teardown_script": _teardown
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"title": "登录接口4",
|
|
355
|
+
"interface": {
|
|
356
|
+
"url": "/member/public/login",
|
|
357
|
+
"method": "post"
|
|
358
|
+
},
|
|
359
|
+
"headers": {
|
|
360
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
361
|
+
"Token": "${token}"
|
|
362
|
+
},
|
|
363
|
+
"request": {
|
|
364
|
+
"params": {},
|
|
365
|
+
"data": {"keywords": "13012349900", "password": "test123",
|
|
366
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
367
|
+
"mobile": "${mobile}"},
|
|
368
|
+
"json": {"keywords": "13012349900", "password": "test123",
|
|
369
|
+
"user2": "${username}", "pwd2": "${password}",
|
|
370
|
+
"mobile": "${mobile}"}
|
|
371
|
+
},
|
|
372
|
+
"setup_script": _setup,
|
|
373
|
+
"teardown_script": ""
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
# 全局环境数据
|
|
380
|
+
test_env_data = {
|
|
381
|
+
"base_url": "http://121.43.169.97:8081",
|
|
382
|
+
"headers": {
|
|
383
|
+
"Content-Type": "application/json"
|
|
384
|
+
},
|
|
385
|
+
"envs": {
|
|
386
|
+
"username": "rand_13012349900",
|
|
387
|
+
"password": "rand_test123",
|
|
388
|
+
"token": "12345678"
|
|
389
|
+
},
|
|
390
|
+
"global_func": _tools,
|
|
391
|
+
"db": [
|
|
392
|
+
{
|
|
393
|
+
"name": "P2P",
|
|
394
|
+
"type": "mysql",
|
|
395
|
+
"config": {
|
|
396
|
+
"host": "121.43.169.97",
|
|
397
|
+
"port": 3306,
|
|
398
|
+
"user": "student",
|
|
399
|
+
"password": "P2P_student_2023"
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
runner = TestRunner(test_env_data)
|
|
406
|
+
result = runner.execute_cases(test_case2)
|
|
407
|
+
log.info_log("测试结果:", result)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# ApiEngine 用例执行引擎层
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AssertionEngine:
|
|
5
|
+
"""断言引擎:支持中英文关键字、智能类型归一化"""
|
|
6
|
+
|
|
7
|
+
METHOD_MAP = {
|
|
8
|
+
# 相等
|
|
9
|
+
"相等": lambda a, b: a == b,
|
|
10
|
+
"equals": lambda a, b: a == b,
|
|
11
|
+
"eq": lambda a, b: a == b,
|
|
12
|
+
"==": lambda a, b: a == b,
|
|
13
|
+
# 相等忽略大小写
|
|
14
|
+
"相等忽略大小写": lambda a, b: a.lower() == b.lower(),
|
|
15
|
+
"equals_ignore_case": lambda a, b: a.lower() == b.lower(),
|
|
16
|
+
"eq_ignore_case": lambda a, b: a.lower() == b.lower(),
|
|
17
|
+
# 不相等
|
|
18
|
+
"不相等": lambda a, b: a != b,
|
|
19
|
+
"not_equals": lambda a, b: a != b,
|
|
20
|
+
"ne": lambda a, b: a != b,
|
|
21
|
+
"!=": lambda a, b: a != b,
|
|
22
|
+
# 包含
|
|
23
|
+
"包含": lambda a, b: a in b,
|
|
24
|
+
"contains": lambda a, b: a in b,
|
|
25
|
+
"in": lambda a, b: a in b,
|
|
26
|
+
# 不包含
|
|
27
|
+
"不包含": lambda a, b: a not in b,
|
|
28
|
+
"not_contains": lambda a, b: a not in b,
|
|
29
|
+
"not_in": lambda a, b: a not in b,
|
|
30
|
+
# 大于
|
|
31
|
+
"大于": lambda a, b: a > b,
|
|
32
|
+
"greater_than": lambda a, b: a > b,
|
|
33
|
+
"gt": lambda a, b: a > b,
|
|
34
|
+
">": lambda a, b: a > b,
|
|
35
|
+
# 小于
|
|
36
|
+
"小于": lambda a, b: a < b,
|
|
37
|
+
"less_than": lambda a, b: a < b,
|
|
38
|
+
"lt": lambda a, b: a < b,
|
|
39
|
+
"<": lambda a, b: a < b,
|
|
40
|
+
# 大于等于
|
|
41
|
+
"大于等于": lambda a, b: a >= b,
|
|
42
|
+
"greater_than_or_equals": lambda a, b: a >= b,
|
|
43
|
+
"ge": lambda a, b: a >= b,
|
|
44
|
+
">=": lambda a, b: a >= b,
|
|
45
|
+
# 小于等于
|
|
46
|
+
"小于等于": lambda a, b: a <= b,
|
|
47
|
+
"less_than_or_equals": lambda a, b: a <= b,
|
|
48
|
+
"le": lambda a, b: a <= b,
|
|
49
|
+
"<=": lambda a, b: a <= b,
|
|
50
|
+
# 正则匹配
|
|
51
|
+
"正则匹配": lambda a, b: re.search(a, b),
|
|
52
|
+
"regex_match": lambda a, b: re.search(a, b),
|
|
53
|
+
"regex": lambda a, b: re.search(a, b),
|
|
54
|
+
"match": lambda a, b: re.search(a, b),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _normalize_for_compare(expect, actual):
|
|
59
|
+
"""
|
|
60
|
+
智能类型归一化,解决 str/int/float 类型不一致导致的断言失败
|
|
61
|
+
"""
|
|
62
|
+
if type(expect) == type(actual):
|
|
63
|
+
return expect, actual
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
if isinstance(actual, (int, float)) and isinstance(expect, str):
|
|
67
|
+
if '.' in str(expect):
|
|
68
|
+
return float(expect), actual
|
|
69
|
+
else:
|
|
70
|
+
return int(expect), actual
|
|
71
|
+
elif isinstance(expect, (int, float)) and isinstance(actual, str):
|
|
72
|
+
if '.' in str(actual):
|
|
73
|
+
return expect, float(actual)
|
|
74
|
+
else:
|
|
75
|
+
return expect, int(actual)
|
|
76
|
+
except (ValueError, TypeError):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
return expect, actual
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def assert_value(cls, method, expect, actual, debug_log=None, info_log=None, error_log=None):
|
|
83
|
+
"""
|
|
84
|
+
执行断言
|
|
85
|
+
:param method: 断言比较方式
|
|
86
|
+
:param expect: 预期结果
|
|
87
|
+
:param actual: 实际结果
|
|
88
|
+
"""
|
|
89
|
+
expect, actual = cls._normalize_for_compare(expect, actual)
|
|
90
|
+
|
|
91
|
+
assert_fun = cls.METHOD_MAP.get(method)
|
|
92
|
+
if assert_fun is None:
|
|
93
|
+
raise Exception("不支持的断言方法")
|
|
94
|
+
else:
|
|
95
|
+
if debug_log:
|
|
96
|
+
debug_log(f"断言比较方法是:{method}")
|
|
97
|
+
debug_log(f"预期结果是:{expect}")
|
|
98
|
+
debug_log(f"实际结果是:{actual}")
|
|
99
|
+
try:
|
|
100
|
+
assert assert_fun(expect, actual)
|
|
101
|
+
except AssertionError:
|
|
102
|
+
msg = f"断言失败,实际结果({actual}) 不满足({method}) 期望结果({expect})"
|
|
103
|
+
if error_log:
|
|
104
|
+
error_log(msg)
|
|
105
|
+
raise AssertionError(msg)
|
|
106
|
+
else:
|
|
107
|
+
if info_log:
|
|
108
|
+
info_log(f"断言成功,实际结果({actual}) 满足({method}) 期望结果({expect})")
|