基于上一章的动态生成优化
main.py
import os
from config.config import setting
from utils.template import Template
from utils.md5 import Md5
import pytest
from utils.type_check import CheckType
def run():
data = os.listdir('data')
m = Md5('case', 'log', 'case_md5.json')
n = Md5('utils', 'log', 'template_md5.json')
filter_list = m.filter()
utils_list = n.filter()
if 'template.py' not in utils_list:
filter_list = []
n.write_md5()
for i in data:
file_path = 'data' + '/' + i
if os.path.isfile(file_path):
temp = 'test_' + i
if temp not in filter_list:
Template.create_test_file(file_path, 'case')
m.write_md5()
if __name__ == "__main__":
import sys
CheckType.check('data', Template)
if sys.argv[1] == 'dev':
setting['baseUrl'] = setting['dev']
elif sys.argv[1] == 'test':
setting['baseUrl'] = setting['test']
elif sys.argv[1] == 'prd':
setting['baseUrl'] = setting['prd']
else:
raise Exception("the command must be like 'python main.py dev | test | prd'")
run()
pytest.main(['-s', '--alluredir=results'])
os.system('allure generate --clean ./results/ -o ./report/')
os.system('allure open -h 127.0.0.1 -p 8883 ./report/')
utils/type_check.py
import json
import os
from pathlib import Path
class CheckType:
@staticmethod
def check(dir_path, fn):
for file_path in Path(dir_path).iterdir():
if file_path.is_file() and fn.check_todo_file(file_path) is False:
import_name = file_path.stem + '_cfg'
file = file_path.stem
try:
module = __import__(f'data.{file}', fromlist=[f'{import_name}'])
cfg = module.__getattribute__(import_name)
if not isinstance(cfg, list):
raise TypeError(f"Message: the type of <{cfg}> must be list")
elif len(cfg) == 0:
raise TypeError(f"Message: the type of <{cfg}> can't be empty")
else:
for j in cfg:
if not isinstance(j.get('config'), dict):
raise TypeError(f"Message: the attribute <config> of <{import_name}> must be exist "
f"and its type must be dict")
elif not isinstance(j.get('req_extract'), (list, type(None))):
raise TypeError(f"Message: the attribute <req_extract> of <{import_name}> must be "
f"list or None")
elif not isinstance(j.get('res_extract'), (list, type(None))):
raise TypeError(f"Message: the attribute <res_extract> of <{import_name}> must be "
f"list or None")
elif not isinstance(j.get('as'), (dict, type(None))):
raise TypeError(f"Message: the attribute <as> of <{import_name}> must be dict or "
f"None")
elif not isinstance(j.get('assert'), dict):
raise TypeError(f"Message: the attribute <assert> of <{import_name}> must be exist "
f"and its type must be dict")
except ModuleNotFoundError as m:
print(f'''Message: The required variable <{import_name}> was not found in the file <{file_path.name}>,
Example: the file name is <homepage.py>, the variable name should be <homepage_cfg>
''')
utils/template.py
import os
class Template:
@staticmethod
def check_todo_file(file_path: str) -> bool:
"""
检查文件内容中是否包含 '# TODO' 字符串
Args:
file_path (str): 文件路径
Returns:
bool: 如果包含 '# TODO' 字符串则返回 True,否则返回 False
"""
with open(file_path, mode='r+', encoding='utf-8') as file:
return '# TODO' in file.read()
@staticmethod
def create_test_file(file_path: str, target_path: str) -> None:
"""
创建测试文件
Args:
file_path (str): 文件路径
target_path (str): 目标路径
"""
if Template.check_todo_file(file_path):
print(f'Message: 发现 <TODO> 标记,文件 <{file_path}> 尚未完成')
return
file_name = os.path.basename(file_path).replace('.py', '')
import_name = f'{file_name}_cfg'
test_file_path = os.path.join(target_path, f'test_{file_name}.py')
with open(test_file_path, mode='w+', encoding='utf-8') as file:
file.write(f'''import pytest
import allure
from utils.http_request import http_request
from data.{file_name} import {import_name}
from utils.parse import parse
from utils.compare import assert_http_response
from utils.format import format_string
store = {{}}
@allure.suite('{file_name}')
class Test_{file_name.capitalize()}:
@allure.sub_suite('{import_name}')
@pytest.mark.parametrize('cfg', {import_name})
def test_{file_name}(self, getCookie, cfg):
format_string(cfg, store)
allure.dynamic.title(cfg['name'])
allure.dynamic.parameter('cfg', cfg)
cfg['config']['headers']['Cookie'] = getCookie
res = http_request(cfg['config'])
allure.dynamic.description(res.text)
parse(cfg, res, store)
assert_http_response(cfg['assert'], res.json())
''')
print(f"Message: 文件 <{test_file_path}> 创建成功")
utils/md5.py
import hashlib
import json
import os
from json import JSONDecodeError
class Md5:
def __init__(self, dir_path, md5_path, file_name):
self.dir_path = dir_path # 目录路径
self.md5_path = md5_path # MD5文件路径
self.file_name = file_name # MD5文件名
if not os.path.exists(md5_path):
os.mkdir(md5_path) # 如果MD5文件路径不存在,则创建该路径
file_path = os.path.join(md5_path, file_name)
if not os.path.exists(file_path):
open(file_path, mode='w+', encoding='utf-8').close() # 如果MD5文件不存在,则创建该文件
def generate_md5(self):
temp = {}
# 如果dir_path是文件而不是目录,则抛出IOError异常
if os.path.isfile(self.dir_path):
raise IOError(f'Message: parameter <dir_path:{self.dir_path}> must be directory')
else:
dir_list = os.listdir(self.dir_path) # 获取目录下的文件列表
if len(dir_list) != 0:
for i in dir_list:
md5 = hashlib.md5() # 创建MD5对象
file_path = os.path.join(self.dir_path, i) # 获取文件路径
if os.path.isfile(file_path): # 如果是文件
with open(file_path, mode='r', encoding='utf-8') as f:
md5.update(f.read().encode(encoding='utf-8')) # 更新MD5值
hex_md5 = md5.hexdigest() # 获取MD5值
temp[i] = hex_md5 # 将文件名和MD5值添加到字典中
return temp # 返回字典
def write_md5(self):
file_path = os.path.join(self.md5_path, self.file_name)
# 将generate_md5()生成的字典写入到文件中
json.dump(self.generate_md5(), open(file_path, mode='w+', encoding='utf-8'))
def read_md5(self):
file_path = os.path.join(self.md5_path, self.file_name)
try:
with open(file_path, mode='r', encoding='utf-8') as f:
# 读取文件中的json数据并返回
return json.load(f)
except JSONDecodeError:
# 如果文件中的json数据解析失败,则返回空字典
return {}
def filter(self):
old_md5 = self.read_md5() # 获取旧的MD5值
new_md5 = self.generate_md5() # 获取新的MD5值
# 返回新旧md5值相同的文件名列表
return [k for k, v in new_md5.items() if k in old_md5 and v == old_md5[k]]