pytest-auto-api2-cli 0.2.0__py3-none-any.whl

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.
Files changed (73) hide show
  1. cli.py +9 -0
  2. common/__init__.py +0 -0
  3. common/setting.py +78 -0
  4. pytest_auto_api2/__init__.py +4 -0
  5. pytest_auto_api2/cli.py +900 -0
  6. pytest_auto_api2/runtime/__init__.py +1 -0
  7. pytest_auto_api2/runtime/api.py +10 -0
  8. pytest_auto_api2/runtime/loader.py +80 -0
  9. pytest_auto_api2_cli-0.2.0.dist-info/METADATA +945 -0
  10. pytest_auto_api2_cli-0.2.0.dist-info/RECORD +73 -0
  11. pytest_auto_api2_cli-0.2.0.dist-info/WHEEL +5 -0
  12. pytest_auto_api2_cli-0.2.0.dist-info/entry_points.txt +2 -0
  13. pytest_auto_api2_cli-0.2.0.dist-info/top_level.txt +6 -0
  14. run.py +85 -0
  15. test_case/Collect/test_collect_addtool.py +40 -0
  16. test_case/Collect/test_collect_delete_tool.py +40 -0
  17. test_case/Collect/test_collect_tool_list.py +40 -0
  18. test_case/Collect/test_collect_update_tool.py +40 -0
  19. test_case/Login/test_login.py +40 -0
  20. test_case/UserInfo/test_get_user_info.py +40 -0
  21. test_case/__init__.py +6 -0
  22. test_case/conftest.py +132 -0
  23. utils/__init__.py +9 -0
  24. utils/assertion/__init__.py +4 -0
  25. utils/assertion/assert_control.py +179 -0
  26. utils/assertion/assert_type.py +141 -0
  27. utils/cache_process/__init__.py +4 -0
  28. utils/cache_process/cache_control.py +89 -0
  29. utils/cache_process/redis_control.py +106 -0
  30. utils/logging_tool/__init__.py +4 -0
  31. utils/logging_tool/log_control.py +84 -0
  32. utils/logging_tool/log_decorator.py +48 -0
  33. utils/logging_tool/run_time_decorator.py +34 -0
  34. utils/mysql_tool/__init__.py +4 -0
  35. utils/mysql_tool/mysql_control.py +175 -0
  36. utils/notify/__init__.py +1 -0
  37. utils/notify/ding_talk.py +153 -0
  38. utils/notify/lark.py +181 -0
  39. utils/notify/send_mail.py +84 -0
  40. utils/notify/wechat_send.py +109 -0
  41. utils/other_tools/__init__.py +0 -0
  42. utils/other_tools/address_detection.py +73 -0
  43. utils/other_tools/allure_data/__init__.py +4 -0
  44. utils/other_tools/allure_data/allure_report_data.py +84 -0
  45. utils/other_tools/allure_data/allure_tools.py +54 -0
  46. utils/other_tools/allure_data/error_case_excel.py +316 -0
  47. utils/other_tools/exceptions.py +47 -0
  48. utils/other_tools/get_local_ip.py +27 -0
  49. utils/other_tools/install_tool/__init__.py +0 -0
  50. utils/other_tools/install_tool/install_requirements.py +91 -0
  51. utils/other_tools/jsonpath_date_replace.py +28 -0
  52. utils/other_tools/models.py +269 -0
  53. utils/other_tools/thread_tool.py +91 -0
  54. utils/read_files_tools/__init__.py +1 -0
  55. utils/read_files_tools/case_automatic_control.py +138 -0
  56. utils/read_files_tools/clean_files.py +19 -0
  57. utils/read_files_tools/excel_control.py +55 -0
  58. utils/read_files_tools/get_all_files_path.py +27 -0
  59. utils/read_files_tools/get_yaml_data_analysis.py +156 -0
  60. utils/read_files_tools/regular_control.py +209 -0
  61. utils/read_files_tools/swagger_for_yaml.py +145 -0
  62. utils/read_files_tools/testcase_template.py +103 -0
  63. utils/read_files_tools/yaml_control.py +86 -0
  64. utils/recording/__init__.py +0 -0
  65. utils/recording/mitmproxy_control.py +225 -0
  66. utils/requests_tool/__init__.py +0 -0
  67. utils/requests_tool/dependent_case.py +273 -0
  68. utils/requests_tool/encryption_algorithm_control.py +80 -0
  69. utils/requests_tool/request_control.py +443 -0
  70. utils/requests_tool/set_current_request_cache.py +73 -0
  71. utils/requests_tool/teardown_control.py +280 -0
  72. utils/times_tool/__init__.py +0 -0
  73. utils/times_tool/time_control.py +87 -0
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/5/10 18:54
5
+ # @Author : 余少琪
6
+ # @Email : 1603453211@qq.com
7
+ # @File :
8
+ # @describe:
9
+ """
10
+
11
+ import socket
12
+
13
+
14
+ def get_host_ip():
15
+ """
16
+ 查询本机ip地址
17
+ :return:
18
+ """
19
+ _s = None
20
+ try:
21
+ _s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
22
+ _s.connect(('8.8.8.8', 80))
23
+ l_host = _s.getsockname()[0]
24
+ finally:
25
+ _s.close()
26
+
27
+ return l_host
File without changes
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/5/10 14:02
5
+ # @Author : 余少琪
6
+ # @Email : 1603453211@qq.com
7
+ # @File : install_requirements
8
+ # @describe: 判断程序是否每次会更新依赖库,如有更新,则自动安装
9
+ """
10
+ import os
11
+ import chardet
12
+ from common.setting import ensure_path_sep
13
+ from utils.logging_tool.log_control import INFO
14
+ from utils import config
15
+
16
+ os.system("pip3 install chardet")
17
+
18
+
19
+ class InstallRequirements:
20
+ """ 自动识别安装最新的依赖库 """
21
+
22
+ def __init__(self):
23
+ self.version_library_comparisons_path = ensure_path_sep("\\utils\\other_tools\\install_tool\\") \
24
+ + "version_library_comparisons.txt"
25
+ self.requirements_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) \
26
+ + os.sep + "requirements.txt"
27
+
28
+ self.mirror_url = config.mirror_source
29
+ # 初始化时,获取最新的版本库
30
+
31
+ # os.system("pip freeze > {0}".format(self.requirements_path))
32
+
33
+ def read_version_library_comparisons_txt(self):
34
+ """
35
+ 获取版本比对默认的文件
36
+ @return:
37
+ """
38
+ with open(self.version_library_comparisons_path, 'r', encoding="utf-8") as file:
39
+ return file.read().strip(' ')
40
+
41
+ @classmethod
42
+ def check_charset(cls, file_path):
43
+ """获取文件的字符集"""
44
+ with open(file_path, "rb") as file:
45
+ data = file.read(4)
46
+ charset = chardet.detect(data)['encoding']
47
+ return charset
48
+
49
+ def read_requirements(self):
50
+ """获取安装文件"""
51
+ file_data = ""
52
+ with open(
53
+ self.requirements_path,
54
+ 'r',
55
+ encoding=self.check_charset(self.requirements_path)
56
+ ) as file:
57
+
58
+ for line in file:
59
+ if "" in line:
60
+ line = line.replace("", "")
61
+ file_data += line
62
+
63
+ with open(
64
+ self.requirements_path,
65
+ "w",
66
+ encoding=self.check_charset(self.requirements_path)
67
+ ) as file:
68
+ file.write(file_data)
69
+
70
+ return file_data
71
+
72
+ def text_comparison(self):
73
+ """
74
+ 版本库比对
75
+ @return:
76
+ """
77
+ read_version_library_comparisons_txt = self.read_version_library_comparisons_txt()
78
+ read_requirements = self.read_requirements()
79
+ if read_version_library_comparisons_txt == read_requirements:
80
+ INFO.logger.info("程序中未检查到更新版本库,已为您跳过自动安装库")
81
+ # 程序中如出现不同的文件,则安装
82
+ else:
83
+ INFO.logger.info("程序中检测到您更新了依赖库,已为您自动安装")
84
+ os.system(f"pip3 install -r {self.requirements_path}")
85
+ with open(self.version_library_comparisons_path, "w",
86
+ encoding=self.check_charset(self.requirements_path)) as file:
87
+ file.write(read_requirements)
88
+
89
+
90
+ if __name__ == '__main__':
91
+ InstallRequirements().text_comparison()
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/5/23 18:27
5
+ # @Author : 余少琪
6
+ # @Email : 1603453211@qq.com
7
+ # @File : jsonpath_date_replace
8
+ # @describe:
9
+ """
10
+
11
+
12
+ def jsonpath_replace(change_data, key_name, data_switch=None):
13
+ """处理jsonpath数据"""
14
+ _new_data = key_name + ''
15
+ for i in change_data:
16
+ if i == '$':
17
+ pass
18
+ elif data_switch is None and i == "data":
19
+ _new_data += '.data'
20
+ elif i[0] == '[' and i[-1] == ']':
21
+ _new_data += "[" + i[1:-1] + "]"
22
+ else:
23
+ _new_data += '[' + '"' + i + '"' + "]"
24
+ return _new_data
25
+
26
+
27
+ if __name__ == '__main__':
28
+ jsonpath_replace(change_data=['$', 'data', 'id'], key_name='self.__yaml_case')
@@ -0,0 +1,269 @@
1
+ import types
2
+ from enum import Enum, unique
3
+ from typing import Text, Dict, Callable, Union, Optional, List, Any
4
+ from dataclasses import dataclass
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class NotificationType(Enum):
9
+ """ 自动化通知方式 """
10
+ DEFAULT = '0'
11
+ DING_TALK = '1'
12
+ WECHAT = '2'
13
+ EMAIL = '3'
14
+ FEI_SHU = '4'
15
+
16
+
17
+ @dataclass
18
+ class TestMetrics:
19
+ """ 用例执行数据 """
20
+ passed: int
21
+ failed: int
22
+ broken: int
23
+ skipped: int
24
+ total: int
25
+ pass_rate: float
26
+ time: Text
27
+
28
+
29
+ class RequestType(Enum):
30
+ """
31
+ request请求发送,请求参数的数据类型
32
+ """
33
+ JSON = "JSON"
34
+ PARAMS = "PARAMS"
35
+ DATA = "DATA"
36
+ FILE = 'FILE'
37
+ EXPORT = "EXPORT"
38
+ NONE = "NONE"
39
+
40
+
41
+ class TestCaseEnum(Enum):
42
+ URL = ("url", True)
43
+ HOST = ("host", True)
44
+ METHOD = ("method", True)
45
+ DETAIL = ("detail", True)
46
+ IS_RUN = ("is_run", True)
47
+ HEADERS = ("headers", True)
48
+ REQUEST_TYPE = ("requestType", True)
49
+ DATA = ("data", True)
50
+ DE_CASE = ("dependence_case", True)
51
+ DE_CASE_DATA = ("dependence_case_data", False)
52
+ CURRENT_RE_SET_CACHE = ("current_request_set_cache", False)
53
+ SQL = ("sql", False)
54
+ ASSERT_DATA = ("assert", True)
55
+ SETUP_SQL = ("setup_sql", False)
56
+ TEARDOWN = ("teardown", False)
57
+ TEARDOWN_SQL = ("teardown_sql", False)
58
+ SLEEP = ("sleep", False)
59
+
60
+
61
+ class Method(Enum):
62
+ GET = "GET"
63
+ POST = "POST"
64
+ PUT = "PUT"
65
+ PATCH = "PATCH"
66
+ DELETE = "DELETE"
67
+ HEAD = "HEAD"
68
+ OPTION = "OPTION"
69
+
70
+
71
+ def load_module_functions(module) -> Dict[Text, Callable]:
72
+ """ 获取 module中方法的名称和所在的内存地址 """
73
+ module_functions = {}
74
+
75
+ for name, item in vars(module).items():
76
+ if isinstance(item, types.FunctionType):
77
+ module_functions[name] = item
78
+ return module_functions
79
+
80
+
81
+ @unique
82
+ class DependentType(Enum):
83
+ """
84
+ 数据依赖相关枚举
85
+ """
86
+ RESPONSE = 'response'
87
+ REQUEST = 'request'
88
+ SQL_DATA = 'sqlData'
89
+ CACHE = "cache"
90
+
91
+
92
+ class Assert(BaseModel):
93
+ jsonpath: Text
94
+ type: Text
95
+ value: Any
96
+ AssertType: Union[None, Text] = None
97
+
98
+
99
+ class DependentData(BaseModel):
100
+ dependent_type: Text
101
+ jsonpath: Text
102
+ set_cache: Optional[Text] = None
103
+ replace_key: Optional[Text] = None
104
+
105
+
106
+ class DependentCaseData(BaseModel):
107
+ case_id: Text
108
+ # dependent_data: List[DependentData]
109
+ dependent_data: Union[None, List[DependentData]] = None
110
+
111
+
112
+ class ParamPrepare(BaseModel):
113
+ dependent_type: Text
114
+ jsonpath: Text
115
+ set_cache: Text
116
+
117
+
118
+ class SendRequest(BaseModel):
119
+ dependent_type: Text
120
+ jsonpath: Optional[Text] = None
121
+ cache_data: Optional[Text] = None
122
+ set_cache: Optional[Text] = None
123
+ replace_key: Optional[Text] = None
124
+
125
+
126
+ class TearDown(BaseModel):
127
+ case_id: Text
128
+ param_prepare: Optional[List["ParamPrepare"]] = None
129
+ send_request: Optional[List["SendRequest"]] = None
130
+
131
+
132
+ class CurrentRequestSetCache(BaseModel):
133
+ type: Text
134
+ jsonpath: Text
135
+ name: Text
136
+
137
+
138
+ class TestCase(BaseModel):
139
+ url: Text
140
+ method: Text
141
+ detail: Text
142
+ # assert_data: Union[Dict, Text] = Field(..., alias="assert")
143
+ assert_data: Union[Dict, Text]
144
+ headers: Union[None, Dict, Text] = {}
145
+ requestType: Text
146
+ is_run: Union[None, bool, Text] = None
147
+ data: Any = None
148
+ dependence_case: Union[None, bool] = False
149
+ dependence_case_data: Optional[Union[None, List["DependentCaseData"], Text]] = None
150
+ sql: Optional[List] = None
151
+ setup_sql: Optional[List] = None
152
+ status_code: Optional[int] = None
153
+ teardown_sql: Optional[List] = None
154
+ teardown: Union[List["TearDown"], None] = None
155
+ current_request_set_cache: Optional[List["CurrentRequestSetCache"]] = None
156
+ sleep: Optional[Union[int, float]] = None
157
+
158
+
159
+ class ResponseData(BaseModel):
160
+ url: Text
161
+ is_run: Union[None, bool, Text]
162
+ detail: Text
163
+ response_data: Text
164
+ request_body: Any
165
+ method: Text
166
+ sql_data: Dict
167
+ yaml_data: "TestCase"
168
+ headers: Dict
169
+ cookie: Dict
170
+ assert_data: Dict
171
+ res_time: Union[int, float]
172
+ status_code: int
173
+ teardown: Optional[List["TearDown"]] = None
174
+ teardown_sql: Union[None, List] = None
175
+ body: Any
176
+
177
+
178
+ class DingTalk(BaseModel):
179
+ webhook: Union[Text, None]
180
+ secret: Union[Text, None]
181
+
182
+
183
+ class MySqlDB(BaseModel):
184
+ switch: bool = False
185
+ host: Union[Text, None] = None
186
+ user: Union[Text, None] = None
187
+ password: Union[Text, None] = None
188
+ port: Union[int, None] = 3306
189
+
190
+
191
+ class Webhook(BaseModel):
192
+ webhook: Union[Text, None]
193
+
194
+
195
+ class Email(BaseModel):
196
+ send_user: Union[Text, None]
197
+ email_host: Union[Text, None]
198
+ stamp_key: Union[Text, None]
199
+ # 收件人
200
+ send_list: Union[Text, None]
201
+
202
+
203
+ class Config(BaseModel):
204
+ project_name: Text
205
+ env: Text
206
+ tester_name: Text
207
+ # Compatible with config values written as either 0 or "0"
208
+ notification_type: Union[Text, int] = '0'
209
+ excel_report: bool
210
+ ding_talk: "DingTalk"
211
+ mysql_db: "MySqlDB"
212
+ mirror_source: Text
213
+ wechat: "Webhook"
214
+ email: "Email"
215
+ lark: "Webhook"
216
+ real_time_update_test_cases: bool = False
217
+ host: Text
218
+ app_host: Union[Text, None]
219
+
220
+
221
+ @unique
222
+ class AllureAttachmentType(Enum):
223
+ """
224
+ allure 报告的文件类型枚举
225
+ """
226
+ TEXT = "txt"
227
+ CSV = "csv"
228
+ TSV = "tsv"
229
+ URI_LIST = "uri"
230
+
231
+ HTML = "html"
232
+ XML = "xml"
233
+ JSON = "json"
234
+ YAML = "yaml"
235
+ PCAP = "pcap"
236
+
237
+ PNG = "png"
238
+ JPG = "jpg"
239
+ SVG = "svg"
240
+ GIF = "gif"
241
+ BMP = "bmp"
242
+ TIFF = "tiff"
243
+
244
+ MP4 = "mp4"
245
+ OGG = "ogg"
246
+ WEBM = "webm"
247
+
248
+ PDF = "pdf"
249
+
250
+
251
+ @unique
252
+ class AssertMethod(Enum):
253
+ """断言类型"""
254
+ equals = "=="
255
+ less_than = "lt"
256
+ less_than_or_equals = "le"
257
+ greater_than = "gt"
258
+ greater_than_or_equals = "ge"
259
+ not_equals = "not_eq"
260
+ string_equals = "str_eq"
261
+ length_equals = "len_eq"
262
+ length_greater_than = "len_gt"
263
+ length_greater_than_or_equals = 'len_ge'
264
+ length_less_than = "len_lt"
265
+ length_less_than_or_equals = 'len_le'
266
+ contains = "contains"
267
+ contained_by = 'contained_by'
268
+ startswith = 'startswith'
269
+ endswith = 'endswith'
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/3/28 10:52
5
+ # @Author : 余少琪
6
+ """
7
+
8
+
9
+ import time
10
+ import threading
11
+
12
+
13
+ class PyTimer:
14
+ """定时器类"""
15
+
16
+ def __init__(self, func, *args, **kwargs):
17
+ """构造函数"""
18
+
19
+ self.func = func
20
+ self.args = args
21
+ self.kwargs = kwargs
22
+ self.running = False
23
+
24
+ def _run_func(self):
25
+ """运行定时事件函数"""
26
+
27
+ _thread = threading.Thread(target=self.func, args=self.args, kwargs=self.kwargs)
28
+ _thread.setDaemon(True)
29
+ _thread.start()
30
+
31
+ def _start(self, interval, once):
32
+ """启动定时器的线程函数"""
33
+
34
+ interval = max(interval, 0.01)
35
+
36
+ if interval < 0.050:
37
+ _dt = interval / 10
38
+ else:
39
+ _dt = 0.005
40
+
41
+ if once:
42
+ deadline = time.time() + interval
43
+ while time.time() < deadline:
44
+ time.sleep(_dt)
45
+
46
+ # 定时时间到,调用定时事件函数
47
+ self._run_func()
48
+ else:
49
+ self.running = True
50
+ deadline = time.time() + interval
51
+ while self.running:
52
+ while time.time() < deadline:
53
+ time.sleep(_dt)
54
+
55
+ # 更新下一次定时时间
56
+ deadline += interval
57
+
58
+ # 定时时间到,调用定时事件函数
59
+ if self.running:
60
+ self._run_func()
61
+
62
+ def start(self, interval, once=False):
63
+ """启动定时器
64
+
65
+ interval - 定时间隔,浮点型,以秒为单位,最高精度10毫秒
66
+ once - 是否仅启动一次,默认是连续的
67
+ """
68
+
69
+ thread_ = threading.Thread(target=self._start, args=(interval, once))
70
+ thread_.setDaemon(True)
71
+ thread_.start()
72
+
73
+ def stop(self):
74
+ """停止定时器"""
75
+
76
+ self.running = False
77
+
78
+
79
+ def do_something(name, gender='male'):
80
+ """执行"""
81
+ print(time.time(), '定时时间到,执行特定任务')
82
+ print('name:%s, gender:%s', name, gender)
83
+ time.sleep(5)
84
+ print(time.time(), '完成特定任务')
85
+
86
+
87
+ timer = PyTimer(do_something, 'Alice', gender='female')
88
+ timer.start(0.5, once=False)
89
+
90
+ input('按回车键结束\n') # 此处阻塞住进程
91
+ timer.stop()
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/3/28 13:22
5
+ # @Author : 浣欏皯鐞?
6
+ """
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Text
10
+
11
+ from common.setting import data_dir_path, test_case_dir_path, resolve_project_path
12
+ from utils.read_files_tools.get_all_files_path import get_all_files
13
+ from utils.read_files_tools.testcase_template import write_testcase_file
14
+ from utils.read_files_tools.yaml_control import GetYamlData
15
+
16
+
17
+ class TestCaseAutomaticGeneration:
18
+ def __init__(self, *, data_dir: Text = None, test_dir: Text = None, force_write: bool = False):
19
+ self.yaml_case_data = None
20
+ self.file_path = None
21
+ self._data_dir = resolve_project_path(data_dir) if data_dir else data_dir_path()
22
+ self._test_dir = resolve_project_path(test_dir) if test_dir else test_case_dir_path()
23
+ self._force_write = force_write
24
+
25
+ @property
26
+ def case_date_path(self) -> Text:
27
+ """Return yaml testcase directory path."""
28
+ return self._data_dir
29
+
30
+ @property
31
+ def case_path(self) -> Text:
32
+ """Return generated pytest testcase directory path."""
33
+ return self._test_dir
34
+
35
+ @property
36
+ def allure_epic(self):
37
+ _allure_epic = self.yaml_case_data.get("case_common").get("allureEpic")
38
+ assert _allure_epic is not None, (
39
+ "鐢ㄤ緥涓?allureEpic 涓哄繀濉」锛岃妫€鏌ョ敤渚嬪唴瀹? 鐢ㄤ緥璺緞锛?%s'" % self.file_path
40
+ )
41
+ return _allure_epic
42
+
43
+ @property
44
+ def allure_feature(self):
45
+ _allure_feature = self.yaml_case_data.get("case_common").get("allureFeature")
46
+ assert _allure_feature is not None, (
47
+ "鐢ㄤ緥涓?allureFeature 涓哄繀濉」锛岃妫€鏌ョ敤渚嬪唴瀹? 鐢ㄤ緥璺緞锛?%s'" % self.file_path
48
+ )
49
+ return _allure_feature
50
+
51
+ @property
52
+ def allure_story(self):
53
+ _allure_story = self.yaml_case_data.get("case_common").get("allureStory")
54
+ assert _allure_story is not None, (
55
+ "鐢ㄤ緥涓?allureStory 涓哄繀濉」锛岃妫€鏌ョ敤渚嬪唴瀹? 鐢ㄤ緥璺緞锛?%s'" % self.file_path
56
+ )
57
+ return _allure_story
58
+
59
+ @property
60
+ def file_name(self) -> Text:
61
+ """
62
+ Convert current yaml file path to relative python path.
63
+ Example: Collect/collect_tool_list.yaml -> Collect/collect_tool_list.py
64
+ """
65
+ relative = os.path.relpath(self.file_path, self.case_date_path)
66
+ filename, ext = os.path.splitext(relative)
67
+ if ext.lower() not in {".yaml", ".yml"}:
68
+ return relative
69
+ return filename + ".py"
70
+
71
+ @property
72
+ def get_test_class_title(self):
73
+ """Generate class name from case filename."""
74
+ _file_name = os.path.split(self.file_name)[1][:-3]
75
+ _name = _file_name.split("_")
76
+ for index in range(len(_name)):
77
+ _name[index] = _name[index].capitalize()
78
+ return "".join(_name)
79
+
80
+ @property
81
+ def func_title(self) -> Text:
82
+ """Generate test function name from case filename."""
83
+ return os.path.split(self.file_name)[1][:-3]
84
+
85
+ @property
86
+ def spilt_path(self):
87
+ path = self.file_name.split(os.sep)
88
+ path[-1] = "test_" + path[-1]
89
+ return path
90
+
91
+ @property
92
+ def get_case_path(self):
93
+ """Return generated target testcase file absolute path."""
94
+ return os.path.join(self.case_path, *self.spilt_path)
95
+
96
+ @property
97
+ def case_ids(self):
98
+ return [key for key in self.yaml_case_data.keys() if key != "case_common"]
99
+
100
+ @property
101
+ def get_file_name(self):
102
+ return self.spilt_path[-1]
103
+
104
+ def mk_dir(self) -> None:
105
+ """Create target test directory if missing."""
106
+ case_dir_path = os.path.split(self.get_case_path)[0]
107
+ if not os.path.exists(case_dir_path):
108
+ os.makedirs(case_dir_path)
109
+
110
+ def get_case_automatic(self) -> None:
111
+ """Generate pytest test files from all yaml cases."""
112
+ data_path = Path(self.case_date_path)
113
+ if not data_path.exists():
114
+ raise FileNotFoundError(f"YAML data directory does not exist: {data_path}")
115
+
116
+ file_paths = get_all_files(file_path=str(data_path), yaml_data_switch=True)
117
+ for file in file_paths:
118
+ if "proxy_data.yaml" in file:
119
+ continue
120
+
121
+ self.yaml_case_data = GetYamlData(file).get_yaml_data()
122
+ self.file_path = file
123
+ self.mk_dir()
124
+ write_testcase_file(
125
+ allure_epic=self.allure_epic,
126
+ allure_feature=self.allure_feature,
127
+ class_title=self.get_test_class_title,
128
+ func_title=self.func_title,
129
+ case_path=self.get_case_path,
130
+ case_ids=self.case_ids,
131
+ file_name=self.get_file_name,
132
+ allure_story=self.allure_story,
133
+ force_write=self._force_write,
134
+ )
135
+
136
+
137
+ if __name__ == "__main__":
138
+ TestCaseAutomaticGeneration().get_case_automatic()
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ # @Time : 2022/4/7 11:56
5
+ # @Author : 余少琪
6
+ """
7
+
8
+ import os
9
+
10
+
11
+ def del_file(path):
12
+ """删除目录下的文件"""
13
+ list_path = os.listdir(path)
14
+ for i in list_path:
15
+ c_path = os.path.join(path, i)
16
+ if os.path.isdir(c_path):
17
+ del_file(c_path)
18
+ else:
19
+ os.remove(c_path)