#!/usr/bin/env python # -*- coding: utf-8 -*- """ # @Time : 2022/3/28 13:22 # @Author : 李晓杰 """ import os import time from typing import Text, Dict from common.setting import ensure_path_sep from utils.read_files_tools.testcase_template import write_testcase_file from utils.read_files_tools.yaml_control import GetYamlData from utils.read_files_tools.get_all_files_path import get_all_files from utils.other_tools.exceptions import ValueNotFoundError class TestCaseAutomaticGeneration: """自动生成自动化测试中的test_case代码""" @staticmethod def case_date_path() -> Text: """返回 yaml 用例文件路径""" return ensure_path_sep("\\data") @staticmethod def case_path() -> Text: """ 存放用例代码路径""" return ensure_path_sep("\\test_case") def file_name(self, file: Text) -> Text: """ 通过 yaml文件的命名,将名称转换成 py文件的名称 :param file: yaml 文件路径 :return: 示例: DateDemo.py """ i = len(self.case_date_path()) yaml_path = file[i:] file_name = None # 路径转换 if '.yaml' in yaml_path: file_name = yaml_path.replace('.yaml', '.py') elif '.yml' in yaml_path: file_name = yaml_path.replace('.yml', '.py') return file_name def get_case_path(self, file_path: Text) -> tuple: """ 根据 yaml 中的用例,生成对应 testCase 层代码的路径 :param file_path: yaml用例路径 :return: D:\\Project\\test_case\\test_case_demo.py, test_case_demo.py """ # 这里通过“\\” 符号进行分割,提取出来文件名称 os.sep表示分隔符/或者\.windows 就是\\ path = self.file_name(file_path).split(os.sep) # 判断生成的 testcase 文件名称,需要以test_ 开头 (就是在加了个test_在前面) case_name = path[-1] = path[-1].replace(path[-1], "test_" + path[-1]) new_name = os.sep.join(path) # test_case目录必须在根目录pytest_auto_api2下 return ensure_path_sep("\\test_case" + new_name), case_name def get_test_class_title(self, file_path: Text) -> Text: """ 自动生成类名称 :param file_path: :return: sup_apply_list --> SupApplyList 1.传入F:\JACK\My_project\pytest-auto-api2\data\Collect\collect_addtool.yaml 2.通过self.file_name(file_path) -->Collect\collect_addtool.py 3.然后os.path.split 返回一个目录和文件名 Collect,collect_addtool.py (字典) 4.通过[1][:-3] 取到第二个文件,去除.py -->collect_addtool 5.split 按"_"分隔-> colect,addtool 6.len() 获取长度 "2" 7.按长度循环遍历,把每个字符字母变大写capitalize() 将字符串的第一个字母变成大写,其他字母变小写 8.然后再拼接回去,返回 """ # 提取文件名称 _file_name = os.path.split(self.file_name(file_path))[1][:-3] _name = _file_name.split("_") _name_len = len(_name) # 将文件名称格式,转换成类名称: sup_apply_list --> SupApplyList for i in range(_name_len): _name[i] = _name[i].capitalize() _class_name = "".join(_name) # CollectToolList 'str' return _class_name @staticmethod def error_message(param_name, file_path): """ 用例中填写不正确的相关提示 :return: """ msg = f"用例中未找到 {param_name} 参数值,请检查新增的用例中是否填写对应的参数内容" \ "如已填写,可能是 yaml 参数缩进不正确\n" \ f"用例路径: {file_path}" return msg def func_title(self, file_path: Text) -> Text: """ 函数名称 :param file_path: yaml 用例路径 :return: """ _file_name = os.path.split(self.file_name(file_path))[1][:-3] return _file_name @staticmethod def allure_epic(case_data: Dict, file_path) -> Text: """ 用于 allure 报告装饰器中的内容 @allure.epic("项目名称") :param file_path: 用例路径 :param case_data: 用例数据 :return: """ try: return case_data['case_common']['allureEpic'] except KeyError as exc: raise ValueNotFoundError(TestCaseAutomaticGeneration.error_message( param_name="allureEpic", file_path=file_path )) from exc @staticmethod def allure_feature(case_data: Dict, file_path) -> Text: """ 用于 allure 报告装饰器中的内容 @allure.feature("模块名称") :param file_path: :param case_data: :return: """ try: return case_data['case_common']['allureFeature'] except KeyError as exc: raise ValueNotFoundError(TestCaseAutomaticGeneration.error_message( param_name="allureFeature", file_path=file_path )) from exc @staticmethod def allure_story(case_data: Dict, file_path) -> Text: """ 用于 allure 报告装饰器中的内容 @allure.story("测试功能") :param file_path: :param case_data: :return: """ try: return case_data['case_common']['allureStory'] except KeyError as exc: raise ValueNotFoundError(TestCaseAutomaticGeneration.error_message( param_name="allureStory", file_path=file_path )) from exc def mk_dir(self, file_path: Text) -> None: """ 判断生成自动化代码的文件夹路径是否存在,如果不存在,则自动创建 """ # _LibDirPath = os.path.split(self.libPagePath(filePath))[0] _case_dir_path = os.path.split(self.get_case_path(file_path)[0])[0] if not os.path.exists(_case_dir_path): os.makedirs(_case_dir_path) @staticmethod def case_ids(test_case): """ 获取用例 ID :param test_case: 测试用例内容 :return: """ ids = [] for k, v in test_case.items(): if k != "case_common": ids.append(k) # 把yaml中所有的key 都放在ids 的列表内 return ids def yaml_path(self, file_path: Text) -> Text: """ 生成动态 yaml 路径, 主要处理业务分层场景 :param file_path: 如业务有多个层级, 则获取到每一层/test_demo/DateDemo.py :return: Login/common.yaml """ i = len(self.case_date_path()) # 兼容 linux 和 window 操作路径 yaml_path = file_path[i:].replace("\\", "/") return yaml_path def get_case_automatic(self) -> None: """ 自动生成 测试代码""" """ 1. 获取data文件下yaml文件的所有绝对路径 2. 判断是否有'proxy_data.yaml'文件,否才生成测试代码 3. 遍历调用mk_dir():里面会判断test_case 是否有test_*.py文件,没有则调用os.makedirs自动生成 4. GetYamlDate(file) """ # 返回的是路径一个字典 file_path = get_all_files(file_path=ensure_path_sep("\\data"), yaml_data_switch=True) for file in file_path: # 判断代理拦截的yaml文件,不生成test_case代码 if 'proxy_data.yaml' not in file: # 判断用例需要用的文件夹路径是否存在,不存在则创建 self.mk_dir(file) yaml_case_process = GetYamlData(file).get_yaml_data() self.case_ids(yaml_case_process) write_testcase_file( allure_epic=self.allure_epic(case_data=yaml_case_process, file_path=file), allure_feature=self.allure_feature(yaml_case_process, file_path=file), # file :data\\下面的文件 class_title=self.get_test_class_title(file), func_title=self.func_title(file), # D:\\Project\\test_case\\test_case_demo.py case_path=self.get_case_path(file)[0], # yaml_case_process 是一个yaml 字典 case_ids=self.case_ids(yaml_case_process), # test_case_demo.dy file_name=self.get_case_path(file)[1], allure_story=self.allure_story(case_data=yaml_case_process, file_path=file) ) if __name__ == '__main__': TestCaseAutomaticGeneration().get_case_automatic()