class Testtest: ComLog().use_log() yaml_path = ComConfig().test_params_path() # 获取指定测试用例的用例信息 test_params = ComParams().test_params(yaml_path, yaml_name="test.yml") @allure.title("{title}") @pytest.mark.parametrize("param, title", test_params) def test_login(self, param, title): result = ComManage().assert_manner(param) assert result
class ComYaml: ComLog().use_log() @staticmethod def __read_yaml(yaml_path): """ 读取yaml文件 :param yaml_path: yaml文件路径 :return: {} """ try: dict_data = yaml.load(open(yaml_path, 'r', encoding="utf-8"), Loader=yaml.FullLoader) return dict_data except Exception as es: logging.error(F'读取{yaml_path}文件出错,错误是{es}') raise (F'读取{yaml_path}文件出错,错误是{es}') def read_yaml(self, yml_path): """ 遍历文件夹下的所有yaml文件,拆分其中的dec和parameters,并设置为字典的键值对,返回字典 :param yml_path: yaml文件路径 或 yaml文件路径 :return: {dec1:parameters1, dec2:parameters2} """ values_dict = {} # 判断路径是否是文件夹、获取该文件夹下所有的yml文件、并遍历 try: if Path(yml_path).is_dir(): for file in [x for x in list(Path(yml_path).glob("**/*.yml"))]: data_dict = self.__read_yaml(file) for test_name, parameters in data_dict.items(): values_dict[test_name] = parameters elif str(yml_path).endswith(".yml"): file = yml_path # 为了log服务才这样赋值的 data_dict = self.__read_yaml(file) for test_name, parameters in data_dict.items(): values_dict[test_name] = parameters except Exception as es: logging.error(F"解析{file}文件内容出错,错误是{es}") raise Exception return values_dict
def __init__(self): ComLog().use_log()
""" import requests import json import logging from common.com_log import ComLog import urllib3 import allure """ requests常用方法 只支持http/https,以及get、post请求 """ # 忽略InsecureRequestWarning urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) ComLog().use_log() class ComRequest: req_session = requests.session() def send_request(self, request_data): global response try: self.url = request_data["url"].strip() except Exception as es: logging.error(F"请求数据的url获取失败,请求数据是:{request_data},错误是{es}") raise (F"请求数据的url获取失败,请求数据是:{request_data},错误是{es}") try: method = request_data["method"]
class ComAssert: ComLog().use_log() def eq(self, ex, re): try: assert str(ex) == str(re) return True except Exception as es: logging.error(F"eq判断失败,预期结果:{ex},实际结果:{re}") raise (F"eq相等判断失败,预期结果:{ex},实际结果:{re}") def contains(self, ex, re): try: assert str(ex) in str(re) return True except Exception as es: logging.error(F"contains判断失败,预期结果:{ex},实际结果:{re}") raise (F"contains判断失败,预期结果:{ex},实际结果:{re}") def not_contains(self, ex, re): try: assert str(ex) not in str(re) return True except Exception as es: logging.error(F"not_contains判断失败,预期结果:{ex},实际结果:{re}") raise (F"not_contains判断失败,预期结果:{ex},实际结果:{re}") def lt(self, ex, re): try: if isinstance(re, int) or isinstance(re, float): assert float(ex) < float(re) else: assert float(ex) < float(len(re)) return True except Exception as es: logging.error(F"lt判断失败,预期结果:{ex},实际结果:{re}") raise (F"lt判断失败,预期结果:{ex},实际结果:{re}") def le(self, ex, re): try: # assert float(ex) <= float(re) if isinstance(re, int) or isinstance(re, float): assert float(ex) <= float(re) else: assert float(ex) <= float(len(re)) return True except Exception as es: logging.error(F"le判断失败,预期结果:{ex},实际结果:{re}") raise (F"le判断失败,预期结果:{ex},实际结果:{re}") def gt(self, ex, re): try: # assert float(ex) > float(re) if isinstance(re, int) or isinstance(re, float): assert float(ex) > float(re) else: assert float(ex) > float(len(re)) return True except Exception as es: logging.error(F"gt判断失败,预期结果:{ex},实际结果:{re}") raise (F"gt判断失败,预期结果:{ex},实际结果:{re}") def ge(self, ex, re): try: # assert float(ex) >= float(re) if isinstance(re, int) or isinstance(re, float): assert float(ex) >= float(re) else: assert float(ex) >= float(len(re)) return True except Exception as es: logging.error(F"ge判断失败,预期结果:{ex},实际结果:{re}") raise (F"ge判断失败,预期结果:{ex},实际结果:{re}") def sw(self, ex, re): try: assert str(ex).startswith(str(re)) return True except Exception as es: logging.error(F"sw判断失败,预期结果:{ex},不以{re}开头") raise (F"sw判断失败,预期结果:{ex},不以{re}开头") def ew(self, ex, re): try: assert str(ex).endswith(str(re)) return True except Exception as es: logging.error(F"ew判断失败,预期结果:{ex},不以{re}结尾") raise (F"ew判断失败,预期结果:{ex},不以{re}结尾") def assert_result(self, assert_type, ex, re): """ 根据判断类型调用对应的判断方法 :param assert_type: :param ex: :param re: :return: """ try: if assert_type == "eq": return self.eq(ex, re) elif assert_type == "contains": return self.contains(ex, re) elif assert_type == "not_contains": return self.not_contains(ex, re) elif assert_type == "lt": return self.lt(ex, re) elif assert_type == "not_contains": return self.not_contains(ex, re) elif assert_type == "le": return self.le(ex, re) elif assert_type == "gt": return self.gt(ex, re) elif assert_type == "ge": return self.ge(ex, re) elif assert_type == "sw": return self.sw(ex, re) elif assert_type == "ew": return self.ew(ex, re) except Exception as es: logging.error(F"出现了非法的比较类型或者比较结果False:{assert_type}") raise (F"出现了非法的比较类型或者比较结果False:{assert_type}") def assert_code(self, assert_type, ex, response): """ 判断response.status_code是否如预期 :param assert_type: :param ex: :param response: :return: """ try: ex_code = ex[1] re_code = response.status_code return self.assert_result(assert_type, ex_code, re_code) except Exception as es: logging.error( F"code判断失败,判断类型是{assert_type}, 预期值:{ex_code}, 实际值:{re_code}") raise ( F"code判断失败,判断类型是{assert_type}, 预期值:{ex_code}, 实际值:{re_code}") def assert_headers(self, assert_type, ex_value, response): """ 判断headers的内容是否如预期 :param assert_type: :param ex_value: :param response: :return: """ try: ex_headers = ex_value[1] headers_key = str(ex_value[0]).split(".")[1] re_headers = response.headers[headers_key] return self.assert_result(assert_type, ex_headers, re_headers) except Exception as es: logging.error( F"headers判断失败,判断类型是{assert_type}, 预期值:{ex_headers}, headers的key是:{headers_key},实际值:{re_headers}" ) raise ( F"headers判断失败,判断类型是{assert_type}, 预期值:{ex_headers}, headers的key是:{headers_key},实际值:{re_headers}" ) @staticmethod def dict_value(key, data): """ 根据key层级获取data对应的value;jsonpath_rw的方法也可以实现(jsonpath-rw无法安装时用) :param key: str:"one.two.three..." :param data: dict :return: """ key_list = str(key).split(".") for now_key in key_list: if len(key_list) == 1: value = data[now_key] return value else: new_data = data[now_key] key = str.join(".", key_list[1:]) return ComAssert.dict_value(key, new_data) # 递归别忘了返回 def assert_content(self, assert_type, ex_value, response): """ 判断content的内容是否如预期 :param assert_type: :param ex_value: :param response: :return: """ try: # 把"content.xx.yy"转成["xx.yy"]再转成"xx.yy" keys = str(ex_value[0].split("content.")[1:][0]) ex_content = ex_value[1] json_content = json.loads(response.content) # byte转dict # jsonpath_rw,根据key("xx.yy.zz"),返回dict中该key的值 re_content = [ match.value for match in parse(str(keys)).find(json_content) ][0] return self.assert_result(assert_type, ex_content, re_content) except Exception as es: logging.error( F"content判断失败或者响应文本不是json格式,判断类型是{assert_type}, 预期值:{ex_content}, content的key是:{keys},实际值:{re_content}" ) raise ( F"content判断失败或者响应文本不是json格式,判断类型是{assert_type}, 预期值:{ex_content}, content的key是:{keys},实际值:{re_content}" )
class ComManage: ComLog().use_log() def __init__(self): self.com_assert = ComAssert() self.config = ComConfig() self.com_params = ComParams() def validate_manner(self, validates_dict): """ 处理用例数据中的validates数据 :param validates_dict: :return: """ try: ex_dict = dict() # 类型 eq_list = [] # 相等 contains_list = [] # 包含 not_contains_list = [] # 不包含 lt_list = [] # 小于 le_list = [] # 小于等于 gt_list = [] # 大于 ge_list = [] # 大于等于 sw_list = [] # 以xx开头 ew_list = [] # 以xx结尾 for validate in eval(validates_dict): if "eq" in validate: eq_list.append(validate["eq"]) elif "contains" in validate: contains_list.append(validate["contains"]) elif "not_contains" in validate: not_contains_list.append(validate["not_contains"]) elif "lt" in validate: lt_list.append(validate["lt"]) elif "le" in validate: le_list.append(validate["le"]) elif "gt" in validate: gt_list.append(validate["gt"]) elif "ge" in validate: ge_list.append(validate["ge"]) elif "sw" in validate: sw_list.append(validate["sw"]) elif "ew" in validate: ew_list.append(validate["ew"]) ex_dict["eq"] = eq_list ex_dict["contains"] = contains_list ex_dict["not_contains"] = not_contains_list ex_dict["lt"] = lt_list ex_dict["le"] = le_list ex_dict["gt"] = gt_list ex_dict["ge"] = ge_list ex_dict["sw"] = sw_list ex_dict["ew"] = ew_list return ex_dict except Exception as es: logging.error(F"解析validate数据失败,数据为:{validates_dict}") raise es def request_manner(self, request_dict): """ 发送请求,同时处理依赖数据: 处理variables、variables_data、relevance 执行variables对应的用例,返回响应中relevance对应的值,并赋值给variables_data 替换测试用例parameters中对应的variables_data。然后返回处理过后的parameters 1:先确定需要执行的用例yml中的请求(因为一个yml会存在多个请求内容) 2:判断variables_data的值,在对应用例的param中的relevance的value, 3:再从该用例响应中提取出来 4:接着替换掉之前的测试用例parameters中对应的variables_data。然后返回处理过后的parameters :param request_dict: :return: """ # variables: [{'login': ['basic_id', 'audid']}, {'Basic': ['test_id']}] # variables_data: ['audid', 'basic_id'] # relevance: {'id': 'content.data'} try: if "variables" in request_dict and "variables_data" in request_dict: # 处理variables的内容,去掉$,方便处理 variables_data = request_dict["variables_data"] variables = request_dict["variables"] yaml_path = self.config.test_params_path() for variable in eval(variables): # 获取依赖数据的值 for test_name in variable: # print(F"variablesss:{variable}") yaml_name = str(test_name) + ".yml" variables_value = self.variables_value( yaml_path, yaml_name, variables_data) # 把依赖数据变量替换依赖数据的值 request_dict = self.com_params.replace_request( request_dict, variables_value) response = ComRequest().send_request(request_dict) return response except Exception as e: logging.error(F"请求发送失败,请求信息是:{request_dict}") raise (F"请求发送失败,请求信息是:{request_dict}") # 因为调用了Mannage类的的方法,两个类之间不能互相调用,所以移来这里 def variables_value(self, yaml_path, yaml_name, variables_data): """ 获取依赖数据的值。比如 variables: [{'login': ['data_value', 'code_value']}, {'Basic': ['test_id']}] variables_data: ['data_value', 'code_value'] relevance: {'data_value': 'content.data', 'code_value': 'content.code'} 根据variables,到对应的测试用例yaml(key)中,获取到其中拥有 和variables的value全部一致的relevance的key的测试用例信息 然后执行该测试用例,根据relevance的value,到respon中获取对应的值(依赖测试数据的值) :param yaml_path: :param yaml_name: :param variables_data: :return: """ try: tests_params = ComParams().test_params( yaml_path, yaml_name)[0] # 获取到想要执行用例yml的所有内容 num = 0 # 记录依赖测试用例的请求执行次数,只需要执行一次即可 for test_params in tests_params: if "relevance" in test_params: # 判断存在relevance的请求信息内容 relevance_data = test_params["relevance"] variables_value = [] if self.com_params.list_in_dict( variables_data, eval( relevance_data)): # 获取有全部variables_data的请求信息内容 response = ComManage().request_manner(test_params) num += 1 for variable_key in variables_data: # 在relevance_data中,获取variables_data的key对应的value variable_dict = self.com_params.relevance_value( variable_key, eval(relevance_data)[variable_key], response) variables_value.append(variable_dict) if num >= 1: return variables_value except Exception as es: logging.error(F"被依赖用例{yaml_name}不存在依赖用例所需的依赖数据:{variables_data}") raise (F"被依赖用例{yaml_name}不存在依赖用例所需的依赖数据:{variables_data}") def assert_manner(self, request_dict): """ 根据测试用例的validate部分的判断类型(content/headers/status_code),调用不同的判断方法 :param request_dict: :return: 全通过则返回True """ try: # params = request_dict[0][0] # ex_validates = self.validate_manner(params["validate"]) # response = self.request_manner(params) ex_validates = self.validate_manner(request_dict["validate"]) response = self.request_manner(request_dict) for key in ex_validates: values = ex_validates[key] if len(values) >= 1: asssert_type = key for value in values: value_start = value[0].split(".")[0] if value_start.startswith("status_code"): assert self.com_assert.assert_code( asssert_type, value, response) elif value_start.startswith("headers"): assert self.com_assert.assert_headers( asssert_type, value, response) elif value_start.startswith("content"): assert self.com_assert.assert_content( asssert_type, value, response) return True except Exception as es: logging.error(F"异常判断类型:{key},判断值是{value},响应数据是{response.content}") raise (F"异常判断类型:{key},判断值是{value},响应数据是{response.content}")
def __init__(self): ComLog().use_log() # 报告文件文件夹,防止生成的报告内容叠加 self.time = time.strftime('%m-%d-%H-%M', time.localtime()) self.report_path = ComConfig().get_report_path()